EP225. “data-wp-interactive 核心机制:context 是实例级 state”
🔒 登录后可标记已读先不急着重建 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 的系统里:这是一个标准 HTMLdata-属性(前缀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是随便取的属性名,初始值设成0data-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 核心自己就能做到
contextvsstate的核心区别:context是每个 Block 实例各自独立的局部数据——如果页面上插入了 5 个 Quiz Block 实例,每一个都有自己完全独立、互不干扰的一份context;state则是全局共享的数据——如果需要「所有 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/interactivity) | JS 函数 | 注册跟 HTML 对应的 JS 逻辑仓库 |
getContext()(@wordpress/interactivity) | JS 函数 | 在 actions/callbacks 函数内部读取当前 Block 实例的 context 数据 |
常见坑
data-wp-context的外层用双引号包裹——跟里面 JSON 字符串本身用的双引号冲突,属性值会被截断解析出错,必须外层单引号、内部双引号- HTML 的
data-wp-interactive值跟 JSstore()注册时用的命名空间对不上——两边可以随便取名,但必须完全一致,否则 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