EP138-139. “Deprecated 版本兼容与动态 Block(用 PHP 渲染)”
🔒 登录后可标记已读📌 这两讲是同一节课被截成两个文件(EP138 结尾正好卡在句子中间,EP139 开头逐字接续),内容上是一体的,合并成一篇笔记记录。
先解释「为什么改了 save 函数的 HTML 结构,页面里已插入的旧 Block 会报错」——因为 WordPress 要拿保存过的 HTML 跟当前 save 函数重新生成的结果做比对,对不上就认为数据可能已经不可信;解法是用 deprecated 属性保留一份「旧版本 save/attributes」的存档,让 WordPress 知道这是一次已知的合法变更。但作者随即指出这整套「把内容存成静态字符串」的官方标准做法有个大问题:改了 save 函数不会让网站上已经发布的旧文章自动更新,除非一篇篇手动重新打开保存。所以引出了作者真正偏好的做法——动态 Block(Dynamic Block):JS 的 save 直接返回 null(什么都不存),真正的输出交给 PHP 的 render_callback 函数在每次页面加载时实时生成,这样以后要改样式/逻辑,哪怕已经用在上万篇文章里,改一次 PHP 全部立刻生效。
涉及文件
wp-content/plugins/are-you-paying-attention/src/index.js(修改)wp-content/plugins/are-you-paying-attention/index.php(修改)
代码实现
(过渡阶段,最终被放弃)deprecated 属性的用法示例:
// 顶层配置对象新增:
deprecated: [
{
attributes: { /* 旧版本的 attributes 定义,从当前版本复制一份存档 */ },
save: function (props) {
// 旧版本的 save 函数原样保留在这里
}
}
// 如果之后又要再改一次结构,就在数组最前面再插入新的一个存档对象
]
最终采用的方案——动态 Block:src/index.js 的 save 直接返回 null:
wp.blocks.registerBlockType("ourplugin/are-you-paying-attention", {
title: "Are You Paying Attention?",
icon: "smiley",
category: "common",
attributes: {
skyColor: {type: "string"},
grassColor: {type: "string"}
},
edit: function (props) {
function updateSkyColor(event) {
props.setAttributes({skyColor: event.target.value})
}
function updateGrassColor(event) {
props.setAttributes({grassColor: event.target.value})
}
return (
<div>
<input type="text" placeholder="sky color" value={props.attributes.skyColor} onChange={updateSkyColor} />
<input type="text" placeholder="grass color" value={props.attributes.grassColor} onChange={updateGrassColor} />
</div>
)
},
save: function (props) {
return null
}
})
index.php:改用 init 钩子注册 Block,把渲染工作交给 PHP 的 render_callback:
<?php
/*
Plugin Name: Are You Paying Attention Quiz
Description: Give your readers a multiple choice question.
Version: 1.0
Author: Brad
Author URI: https://www.udemy.com/user/bradschiff/
*/
if( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
class AreYouPayingAttention {
function __construct() {
add_action('init', array($this, 'adminAssets'));
}
function adminAssets() {
wp_register_script('ournewblocktype', plugin_dir_url(__FILE__) . 'build/index.js', array('wp-blocks', 'wp-element'));
register_block_type('ourplugin/are-you-paying-attention', array(
'editor_script' => 'ournewblocktype',
'render_callback' => array($this, 'theHTML')
));
}
function theHTML($attributes) {
ob_start(); ?>
<h3>Today the sky is <?php echo esc_html($attributes['skyColor']) ?> and the grass is <?php echo esc_html($attributes['grassColor']) ?>!</h3>
<?php return ob_get_clean();
}
}
$areYouPayingAttention = new AreYouPayingAttention();
关键改动点:
- 钩子从
enqueue_block_editor_assets换成init:因为不再只是「加载一个 JS 文件」这么简单,还要用 PHP 端的register_block_type()正式注册这个 Block(PHP 侧的注册要在更早的init阶段完成) wp_register_script()(不是wp_enqueue_script()):只是把这个脚本「登记」在案、取个短名,先不立即加载——真正决定什么时候加载它的,是接下来register_block_type()里的editor_script参数register_block_type($name, $args):PHP 端注册的名字(ourplugin/are-you-paying-attention)必须跟 JS 里wp.blocks.registerBlockType()用的完全一致,两边其实是在描述同一个 Block,只是分工不同——JS 负责编辑器里的交互体验,PHP 负责真正的输出内容editor_script:告诉 WordPress 编辑器加载哪个脚本(也就是刚wp_register_script()登记的短名)render_callback:这是动态 Block 的核心——指定一个 PHP 函数,前台每次要显示这个 Block 时,WordPress 都会重新调用这个函数、把它的返回值当成最终 HTML 输出,不再依赖数据库里存的静态字符串
theHTML($attributes):WordPress 调用这个函数时会自动把「这个 Block 实例的属性值」当参数传进来(是一个普通的 PHP 关联数组,不是 JS 的props),可以直接用$attributes['skyColor']取值ob_start()/ob_get_clean()(PHP 的输出缓冲区):解决了「用 PHP 拼一长串带很多动态值的字符串很啰嗦」的问题——ob_start()开始「录制」接下来所有跳出 PHP 直接写的 HTML 内容,中间可以随意用<?php ... ?>插入动态值,最后ob_get_clean()把刚才录制的内容当作字符串返回并清空缓冲区。这样写法上更接近直接写 HTML 模板,而不是拿.一段段拼接字符串- 输出动态值前依然要
esc_html()转义——即使这个场景的攻击面不算大,也延续「凡是动态内容输出到前台就转义」的一贯习惯
Hook / Function 速查
| 名称 | 类型 | 用途 |
|---|---|---|
deprecated(Block 配置属性) | Gutenberg Block API | 保留旧版本 attributes/save 定义,让编辑器能识别并兼容已保存的旧格式内容,避免报错 |
wp_register_script() | WP 内建 function | 只登记一个脚本的短名和来源,不立即加载,交给别处(如 register_block_type 的 editor_script)决定何时加载 |
register_block_type($name, $args) | WP 内建 function | PHP 端注册 Block 类型,render_callback 是实现「动态 Block」的关键参数 |
ob_start() / ob_get_clean() | PHP 内建 function | 输出缓冲区:捕获一段跳出 PHP 直接写的 HTML,最后当字符串取出,比拼接字符串更易读 |
常见坑
- 改了
save函数的 HTML 结构、又没有提供deprecated存档——所有已发布文章里的旧 Block 实例都会报「非预期或无效内容」的错误 - 误以为「改一次静态字符串型 Block 的
save函数,所有用到它的文章会自动更新」——实际上必须逐篇手动重新打开保存才会生效,文章一多几乎不可行,这正是作者放弃这种做法、改用动态 Block 的核心原因 - 动态 Block 忘记同时保留
editor_script——render_callback只负责前台真实输出,编辑器里要能互动编辑(输入框、实时预览)依然需要 JS 的edit函数正常加载
[截图:改动 save 函数结构后,编辑器里旧 Block 实例出现的 "This block contains unexpected or invalid content" 错误提示]
延伸 / 后续讲座会用到
基础机制全部打通,下一讲开始做真正实用的 Block:一个带对错判断的互动问答题(Quiz),再往后是能动态关联教授数据的 Featured Professor Block。
Sources
Udemy:
- Become a WordPress Developer: Unlocking Power With Code — Section 24, EP138-139