WP DEVELOP

EP225. “data-wp-interactive 核心机制:context 是实例级 state”

首页 WordPress 开发课程 INTERACTIVITY API · EP225
约 15 分钟· #EP225#INTERACTIVITY API
🔒 登录后可标记已读

先不急着重建 Quiz 前台交互,用一个最小例子(一个按钮 + 一个点击计数)彻底搞懂 Interactivity API 的核心运作原理:HTML 里用几个 data-wp-* 属性声明「这段标记要跟哪个 JS store 关联」「点击时触发哪个 action」「用哪份 context 数据渲染文字」;JS 侧用 store() 注册一个跟 HTML 里 data-wp-interactive 值相匹配的命名空间,actions 对象里定义具体的处理函数,函数内部用 getContext() 拿到当前这个 Block 实例自己的一份数据、直接修改它,页面就会自动重新渲染。这一讲也讲清楚 context(实例级 state)跟 state(全局 state)的区别,并且演示了一个让人非常兴奋的效果:在浏览器完全没跑任何 JS 之前,服务器端就已经把 context 的初始值直接渲染进了 HTML 里(查看网页源代码能看到 <span> 不是空的),做到了「客户端交互 + 服务器端渲染」两者兼得。


涉及文件

  • wp-content/plugins/interactive-quiz/src/render.php (修改)
  • wp-content/plugins/interactive-quiz/src/view.js (修改)

代码实现

src/render.php(把脚手架自带的复杂占位内容整个删掉,从零手写一个最小例子)

<?php
/**
 * PHP file to use when rendering the block type on the server to show on the front end.
 * ...(顶部这段注释是脚手架自带的说明,保留即可)
 */
?>

<div data-wp-interactive="create-block" data-wp-context='{"clickCount": 0}'>
	<p>The button below has been clicked <span data-wp-text="context.clickCount"></span> times.</p>
	<button data-wp-on--click="actions.buttonHandler">Click me</button>
</div>

src/view.js(完整文件)

import { store, getContext } from "@wordpress/interactivity"

store("create-block", {
  actions: {
    buttonHandler: () => {
      const context = getContext()
      context.clickCount++
    },
    toggle: () => {
      const context = getContext()
      context.isOpen = !context.isOpen
    }
  },
  callbacks: {
    logIsOpen: () => {
      const { isOpen } = getContext()
      // Log the value of `isOpen` each time it changes.
      console.log(`Is open: ${isOpen}`)
    }
  }
})

关键改动点:

  • 不深挖脚手架自带的示例代码,直接从零手写:作者提到「与其读别人写好的样板代码,不如自己从空白开始敲一遍,理解得更深」——先把 render.php 里脚手架生成的那一大段占位内容整个删掉,只留最外层需要的 PHP 收尾标签,自己手写一个只有「一段文字 + 一个按钮」的最简单结构
  • data-wp-interactive="create-block"——把这段 HTML「注册」进 Interactivity API 的系统里:这是一个标准 HTML data- 属性(前缀 data- 本身就是网页标准语法,可以自由命名 data-pizza/data-unicorn 之类),WordPress 定义了一整套以 data-wp- 开头的属性名(wp-on/wp-context/wp-text/wp-bind/wp-class/wp-watch 等),赋予它们特殊含义。data-wp-interactive 的值必须跟 JS 里 store() 注册时用的命名空间完全一致(这里是 create-block,脚手架默认给的名字,不强制要求叫这个,改成别的名字也可以,只要 HTML/JS 两边对得上)——只有这个值匹配,WordPress 才知道「这段 HTML 应该由哪个 JS Store 来驱动」
  • data-wp-on--click="actions.buttonHandler"——声明点击事件的处理函数data-wp-on 后面用两个短横线 -- 接具体事件名(这里是 click,其他可能有 keyup/hover/scroll 等),值指向 JS Store 里 actions 对象下的某个方法名——写法类似「路径」,actions.buttonHandler 就是去 store() 注册对象的 actions 属性里找 buttonHandler 这个函数
  • data-wp-context='{"clickCount": 0}'——声明这个 Block 实例自己的局部数据:注意这里外层用单引号,内部 JSON 字符串的键名用双引号——这是这一讲实测出来的语法要求(一开始用双引号包外层导致完全不生效,改成单引号包外层、双引号包 JSON 内部字符串才正确工作);clickCount 是随便取的属性名,初始值设成 0
  • data-wp-text="context.clickCount"——把 context 里的某个值渲染到元素文字内容里:放在一个空的 <span> 标签的开始标签上,WordPress 会自动把 context.clickCount 的当前值填进这个 <span> 的文字内容
  • getContext()——在 JS 里访问「当前这个 Block 实例」的 context 数据context 不是随手就能读到的全局变量,必须调用 WordPress 提供的这个工具函数才能拿到——const context = getContext() 之后,context.clickCount++ 直接修改这份数据,WordPress 会自动检测到变化、重新渲染画面上引用了这个值的地方,不需要手动触发任何「重新渲染」的调用
  • 验证「服务器端预渲染」效果的关键操作:右键页面选择「查看网页源代码」(不是浏览器开发者工具里的「检查元素」,两者不同——前者是纯服务器返回的原始 HTML,后者是经过 JS 执行后的最终 DOM),搜索能找到 <span> 标签已经不是空的,里面已经写好了 context.clickCount 的初始值——这说明 WordPress 在服务器端渲染阶段就已经算好了 context 的初始状态、直接输出进静态 HTML,浏览器加载完 JS 之后只是「接管」这份已经存在的内容,不需要等 JS 跑完才能看到正确内容
  • 这一点技术意义重大:作者认为这终于让 WordPress 同时具备「客户端交互能力」和「服务器端渲染带来的速度/无障碍/SEO 优势」——过去要实现这种效果通常要借助 Next.js 这类专门的服务器端渲染框架做一个 headless 站点,现在 WordPress 核心自己就能做到
  • context vs state 的核心区别context每个 Block 实例各自独立的局部数据——如果页面上插入了 5 个 Quiz Block 实例,每一个都有自己完全独立、互不干扰的一份 contextstate 则是全局共享的数据——如果需要「所有 Quiz 实例共用的一个统计数字」(比如页面右上角显示「你已经答对了 3 道题」,这个数字要跨多个 Block 实例累计),就需要用 state 而不是 context。作者提醒:九成场景(尤其是「针对某一个具体 Block 实例的数据」)应该优先想到用 context,只有真正需要跨实例共享的数据才用 state

Hook / Function 速查

名称类型用途
data-wp-interactive="命名空间"Interactivity API HTML 属性把一段 HTML 关联到 JS 里同名的 store()
data-wp-context='{"键":值}'Interactivity API HTML 属性声明该 Block 实例自己的局部数据(外层单引号,内部 JSON 用双引号)
data-wp-text="context.属性名"Interactivity API HTML 属性context 里的值渲染进元素的文字内容
data-wp-on--{事件名}="actions.方法名"Interactivity API HTML 属性声明某个 DOM 事件触发时要调用 Store 里 actions 下的哪个方法
store(命名空间, {actions, callbacks, ...})@wordpress/interactivityJS 函数注册跟 HTML 对应的 JS 逻辑仓库
getContext()@wordpress/interactivityJS 函数actions/callbacks 函数内部读取当前 Block 实例的 context 数据

常见坑

  • data-wp-context 的外层用双引号包裹——跟里面 JSON 字符串本身用的双引号冲突,属性值会被截断解析出错,必须外层单引号、内部双引号
  • HTML 的 data-wp-interactive 值跟 JS store() 注册时用的命名空间对不上——两边可以随便取名,但必须完全一致,否则 WordPress 无法把这段 HTML 和对应的 JS 逻辑关联起来
  • 直接在 JS 里访问一个名叫 context 的变量而不调用 getContext()——context 不是自动可用的全局量,必须显式调用这个工具函数才能拿到当前实例的数据
  • 把「应该属于单个 Block 实例的数据」错误地放进全局 state——不同实例之间的数据会互相污染、共享,而不是各自独立

[截图:前台页面点击 Click me 按钮后,旁边文字里的点击次数实时递增的效果,以及"查看网页源代码"里 span 标签已带有初始值 0 的原始 HTML]


延伸 / 后续讲座会用到

下一讲开始正式重建 Quiz Block 的前台判分交互界面,会用到这一讲学到的这几个核心概念。


Sources

Udemy:

  • Become a WordPress Developer: Unlocking Power With Code — Section 30, EP225