搞WordPress块里咋整动态内容,从外面API拽数据到前端显示?

4,998字
21–32 分钟
in

摘要

目录

咱今儿聊点实在的,WordPress自带那个块编辑器(Gutenberg)里头,光弄个静态内容太没劲了。要是能从外面那些数据接口(API)直接拉点实时信息,比如足球排名、股票价格啥的,那才叫带劲。这篇手把手教大家怎么鼓捣一个自定义块,利用API拿数据,存进数据库,最后在前端页面给整得明明白白。整个过程不用慌,跟着敲代码就完事了。

啥是动态块和外部API

WordPress里头的“块”就是编辑页面时拖来拖去那些小零件,比如段落、图片。普通块存进数据库的是固定内容,换个说法叫静态渲染。动态块就牛了,每次有人打开页面,它都能重新从别处抓最新数据来显示,比如从足球数据网站拉个实时积分榜。

外部API就好比饭店的窗口,这边喊一嗓子“来份英超排名”,那边就把做好的数据(通常是JSON格式)递出来。配合fetch这个工具,WordPress块就能跟外面世界唠上嗑。

用脚手架搭块儿地基

开整之前得先有个干活的环境。推荐用官方那个@wordpress/create-block包,跟装修队似的,唰一下就把项目架子搭好了。

在命令行敲这串:

npx @wordpress/create-block football-rankings

这命令会生出一个叫football-rankings的文件夹,里边已经是一个能直接用的WordPress插件。完事把这个文件夹整个挪到本地网站的wp-content/plugins目录下头,然后去后台“插件”页面点一下“启用”。

这时候新建一篇文章,编辑器里已经能看到“Football Rankings”这个块了。不过现在它还是个空壳子,接下来给它加点真功夫。

从API抓数据的正确姿势

想拿到外部数据,得在edit.js文件里动刀。这个文件管的是编辑器里块长啥样。用useEffect钩子包住请求代码,保证页面加载时只请求一次,免得编辑器每刷新一下就狂轰滥炸API。

打开src/edit.js,先引入useEffect

import { useEffect } from "@wordpress/element";

然后在Edit函数里塞进去这段:

useEffect(() => {
  const options = {
    method: "GET",
    headers: {
      "X-RapidAPI-Key": "你的密钥",
      "X-RapidAPI-Host": "api-football-v1.p.rapidapi.com",
    },
  };

  fetch("https://api-football-v1.p.rapidapi.com/v3/standings?season=2021&league=39", options)
    .then( ( response ) => response.json() )
    .then( ( response ) => {
      let newData = { ...response };
      setAttributes( { data: newData } );
      console.log( "属性里存的数据", attributes );
    })
    .catch((err) => console.error(err));
}, []);

这里有个坑:API密钥得自己去RapidAPI申请,免费的够折腾了。还有那个setAttributes是拿来把拿到的数据存进块的“属性”里,就像给块安了个小书包。

让WordPress记住抓来的数据

数据拿下来了,不存好等于白干。块得有地方放这些宝贝,那就是attributes。在index.js里头注册块的时候得声明一下:

registerBlockType( metadata.name, {
  edit: Edit,
  attributes: {
    data: {
      type: "object",
    },
  },
  save,
} );

指定typeobject特别要紧,因为API返回的是JSON对象。要是写错了类型,WordPress一声不吭就把数据扔了,连个错都不报,那才叫欲哭无泪。存好之后去数据库的wp_posts表里post_content字段翻翻,能看到一串JSON字符串,证明数据已经落袋为安了。

前端页面把数据摆上桌

后端存储搞掂了,该让网站访客也瞅瞅这些排名了。这需要在前端把数据从数据库拽出来,再画成好看的表格。

先在src目录下新建两个文件:frontend.jsfrontend.scss(SCSS编译后变CSS)。然后在package.jsonscripts里改一下构建命令:

"scripts": {
  "build": "wp-scripts build src/index.js src/frontend.js",
  "start": "wp-scripts start src/index.js src/frontend.js"
}

接着在football-rankings.php里注册一个渲染回调:

function create_block_football_rankings_block_init() {
  register_block_type( __DIR__ . '/build', array(
    'render_callback' => 'render_frontend'
  ));
}
add_action( 'init', 'create_block_football_rankings_block_init' );

function render_frontend($attributes) {
  if( !is_admin() ) {
    wp_enqueue_script( 'football_rankings', plugin_dir_url( __FILE__ ) . '/build/frontend.js');
    wp_enqueue_style( 'football_rankings', plugin_dir_url( __FILE__ ) . '/build/frontend.css' );
  }

  ob_start(); ?>
  <div class="football-rankings-frontend" id="league-standings">
    <div class="data" style="display:none;">
      <pre><?php echo wp_json_encode( $attributes ); ?></pre>
    </div>
    <div class="header">
      <div class="position">排名</div>
      <div class="team-logo">队徽</div>
      <div class="team-name">队名</div>
      <div class="stats">
        <div class="games-played">场次</div>
        <div class="games-won">胜</div>
        <div class="games-drawn">平</div>
        <div class="games-lost">负</div>
        <div class="goals-for">进球</div>
        <div class="goals-against">失球</div>
        <div class="points">积分</div>
      </div>
      <div class="form-history">近5场</div>
    </div>
    <div class="league-table"></div>
  </div>
  <?php return ob_get_clean();
}

注意那个隐藏的<div class="data">,它把属性里的JSON数据塞进HTML,方便前端JS去读。这种土法子虽然不优雅,但胜在简单粗暴,新手一看就懂。

用JavaScript把JSON变表格

最后一步,在frontend.js里写逻辑,把隐藏的JSON数据解析出来,动态生成每一行的排名信息。

import "./frontend.scss";

window.addEventListener( "load", () => {
  const dataPre = document.querySelector( ".data pre" );
  if ( !dataPre ) return;
  const dataEl = dataPre.innerHTML;
  const tableEl = document.querySelector( ".league-table" );
  const headerEl = document.querySelector( "#league-standings .header" );
  const dataJSON = JSON.parse( dataEl );

  console.log( "前端拿到数据", dataJSON );

  let teams = dataJSON.data.response[0].league.standings[0];
  let leagueLogo = dataJSON.data.response[0].league.logo;
  headerEl.style.backgroundImage = `url(${leagueLogo})`;

  teams.forEach( ( team, idx ) => {
    const teamDiv = document.createElement("div");
    const { played, win, draw, lose, goals } = team.all;
    teamDiv.classList.add("team");
    teamDiv.innerHTML = `
      <div class="position">${ idx + 1 }</div>
      <div class="team-logo"><img src="${ team.team.logo }" /></div>
      <div class="team-name">${ team.team.name }</div>
      <div class="stats">
        <div class="games-played">${ played }</div>
        <div class="games-won">${ win }</div>
        <div class="games-drawn">${ draw }</div>
        <div class="games-lost">${ lose }</div>
        <div class="goals-for">${ goals.for }</div>
        <div class="goals-against">${ goals.against }</div>
        <div class="points">${ team.points }</div>
      </div>
      <div class="form-history"></div>
    `;

    // 处理近五场战绩,把字符串"WWLDW"拆成一个个字母
    const formArray = team.form.split("");
    formArray.forEach( result => {
      const resultSpan = document.createElement("div");
      resultSpan.classList.add("result");
      resultSpan.innerText = result;
      if ( result === "W" ) resultSpan.classList.add("win");
      else if ( result === "D" ) resultSpan.classList.add("draw");
      else resultSpan.classList.add("lost");
      teamDiv.querySelector(".form-history").appendChild(resultSpan);
    });

    tableEl.appendChild(teamDiv);
  });
});

这里把“W”代表赢、“D”代表平、“L”代表输分别配上不同样式,整得红红绿绿的,一眼就能看出球队最近状态咋样。运行npm run build打包一下,刷新页面,一个活生生的足球排名块就出来了。

步骤关键动作常见翻车点
搭架create-block命令Node版本太低会报错
抓数useEffect里写fetch忘记传空数组导致死循环
存数定义attributes类型为对象类型写错数据就没了
显示写render_callback忘记清空输出缓冲
渲染前端解析JSON生成DOM数据路径不对取不到response[0]

搞定收工,这个套路不光能搞足球排名,换成天气、股票、段子啥的都一样玩得转。