WP DEVELOP

EP147. “前台入口:第二个构建入口与 React 挂载”

首页 WordPress 开发课程 GUTENBERG BLOCK 入门(block.json) · EP147
约 14 分钟· #EP147#GUTENBERG BLOCK 入门(block.json)
🔒 登录后可标记已读

📌 并入 EP146 的提醒:这一讲加载前台 JS 的写法(wp_enqueue_script() 只传 3 个参数),如果用的是传统主题没问题;但如果用的是新式 Block Theme,JS 可能会在页面/DOM 还没加载完时就执行,导致脚本找不到目标 HTML。解法是给 wp_enqueue_script() 补上第 4、5 个参数:wp_enqueue_script('attentionFrontend', ..., array('wp-element'), '1.0', true)——最后的 true 让 WordPress 把这个脚本放到页面底部(footer)加载,这样不管用传统主题还是 Block Theme 都能正常工作。

开始做 Quiz Block 的前台部分:新建一个独立的入口文件 frontend.js(区别于编辑器用的 index.js),要先把 @wordpress/scriptsbuild/start 命令配置成同时处理两个入口文件。前台渲染的思路是:PHP 只负责输出一个空的占位 <div>,真正的内容用前台 JS 里的 React(其实是 WordPress 包装过的同一份 React,不需要额外安装)把一个 Quiz 组件挂载到这个占位 div 里。同时确认了「只在真正用到这个 Block 的页面才加载前台资源」的实现技巧——把加载逻辑放进 render_callback 函数本身,而不是全局无条件加载。


涉及文件

  • wp-content/plugins/are-you-paying-attention/src/frontend.js (新建)
  • wp-content/plugins/are-you-paying-attention/src/frontend.scss (新建,样式文件由课程直接提供下载,内容从略)
  • wp-content/plugins/are-you-paying-attention/package.json (修改,build/start 命令新增第二个入口)
  • wp-content/plugins/are-you-paying-attention/index.php (修改)

代码实现

package.json:让构建命令同时处理两个入口文件

"scripts": {
  "build": "wp-scripts build src/index.js src/frontend.js",
  "start": "wp-scripts start src/index.js src/frontend.js",
  "test": "echo \"Error: no test specified\" && exit 1"
}

src/frontend.js(完整文件,先用占位文字确认渲染链路通了)

import React from 'react'
import ReactDOM from 'react-dom'
import "./frontend.scss"

const divsToUpdate = document.querySelectorAll(".paying-attention-update-me")

divsToUpdate.forEach(function(div) {
  ReactDOM.render(<Quiz />, div)
  div.classList.remove("paying-attention-update-me")
})

function Quiz() {
  return (
    <div className="paying-attention-frontend">
      Hello from React
    </div>
  )
}

index.phptheHTML() 里按需加载前台资源、只输出一个占位 <div>

function theHTML($attributes) {
  if (!is_admin()) {
    wp_enqueue_script('attentionFrontend', plugin_dir_url(__FILE__) . 'build/frontend.js', array('wp-element'));
    wp_enqueue_style('attentionFrontendStyles', plugin_dir_url(__FILE__) . 'build/frontend.css');
  }

  ob_start(); ?>
  <div class="paying-attention-update-me"></div>
  <?php return ob_get_clean();
}

关键改动点:

  • @wordpress/scripts 默认只认 src/index.js 一个入口,要处理第二个独立文件(frontend.js),得在 package.jsonbuild/start 命令后面显式列出两个文件路径——这样编译产物会分别生成 build/index.js(编辑器用)和 build/frontend.js(前台用)两份独立的文件
  • import React from 'react' / import ReactDOM from 'react-dom':即使从没执行过 npm install react,也能直接这样导入——@wordpress/scripts 的 Webpack 配置认识 react/react-dom 这两个包名,会自动转换成「去浏览器全局作用域找 WordPress 已经加载好的对应模块」(也就是 wp.element 背后的那份 React),而不是打包进产物、让访客重复下载一份
  • 前台渲染的核心两行:document.querySelectorAll(".paying-attention-update-me") 找出页面上所有本插件生成的占位 <div>(一个页面可能有多个 Block 实例),.forEach() 遍历后用 ReactDOM.render(<Quiz />, div)Quiz 组件渲染进每一个占位元素——用 CSS class 选择器而不是 ID,正是因为要兼容「同一页面出现多个实例」的情况
  • 渲染完成后用 div.classList.remove("paying-attention-update-me") 把占位 class 去掉——这一步不是必须的,但为将来(比如懒加载场景下,新内容异步插入页面)区分「哪些 div 已经处理过、哪些还没处理」留了扩展空间
  • 只在真正用到这个 Block 的页面才加载前台 JS/CSS 的实现技巧:不是在插件顶层无条件调用 wp_enqueue_script(),而是把加载逻辑写进 render_callback(也就是 theHTML())函数体内部——这个函数只有当前页面真的渲染到了这个 Block 的实例时才会被调用,所以借用它的调用时机来判断「现在需不需要加载前台资源」,比在全站范围判断「当前页面是否包含某个 Block」更简单直接
  • if (!is_admin()) 包住这两行——避免在后台编辑器页面(theHTML()callback 在编辑器预览时也可能被间接触发)重复加载前台专属的资源,前台资源只应该在真正面向公众的页面加载
  • WordPress 会自动去重:即使一个列表页同时显示 5、10 篇都用到这个 Block 的文章、theHTML() 被调用很多次,wp_enqueue_script() 内部靠「短名」判断,同一个资源只会真正加载一次,不会重复加载
  • 用打包产物的文件体积能验证「前台 JS 确实没有打包进 React/ReactDOM 完整副本」——作者提到自己编译出的 frontend.js 只有 7KB 左右,而独立下载的 React + ReactDOM 有 100KB+,证明这份精简靠的正是复用 WordPress 已加载的 wp-element

[截图:前台页面里 Quiz Block 占位 div 已被 React 替换成"Hello from React"文字,确认渲染链路打通]


Hook / Function 速查

名称类型用途
wp-scripts build/start <文件1> <文件2>@wordpress/scripts 命令行参数显式指定多个构建入口文件
is_admin()WP 内建 function判断当前请求是否处于后台管理界面
ReactDOM.render(component, domElement)React API(经 wp-element 提供)把一个 React 组件挂载渲染进指定的真实 DOM 元素
document.querySelectorAll(selector)浏览器原生 API选中所有匹配 CSS 选择器的元素,返回可遍历的 NodeList

常见坑

  • Block Theme 环境下前台 JS 加载时机过早、DOM 还没准备好——wp_enqueue_script() 第 5 个参数传 true 把脚本放到页面底部加载即可解决(EP146 已提醒)
  • 只在全局无条件加载前台资源,不管当前页面用没用到这个 Block——会让完全不相关的页面也白白加载额外的 JS/CSS,浪费访客带宽
  • 前台渲染逻辑用 document.querySelector()(单数,只选中第一个)而不是 querySelectorAll()——一个页面有多个 Block 实例时只会渲染第一个,其余的占位 div 会一直空着

延伸 / 后续讲座会用到

现在前台组件还只是显示占位文字,下一讲要打通「PHP 传下来的真实 attributes 数据」到「前台 React 组件」这条链路,让前台真正显示问题和答案选项。


Sources

Udemy:

  • Become a WordPress Developer: Unlocking Power With Code — Section 25, EP147(含 EP146 提醒并入)