EP183. “用 PHP 类批量注册 Block + 新建 generic-heading”
🔒 登录后可标记已读这一章接下来要创建大量自定义 Block,如果每个 Block 都在 functions.php 里重复写一遍「注册脚本 + 注册 Block 类型」的样板代码会很啰嗦——把这段逻辑抽成一个可复用的 JSXBlock PHP 类,构造函数接收一个名字参数,之后每新增一个 Block 只需要 new JSXBlock('块名') 一行代码。用这个类注册了新的 genericheading Block(先只做占位测试),并把 EP182 里 Banner 的 allowedBlocks 从「允许所有核心标题/段落/列表」收紧成「只允许嵌套这个自己写的 genericheading Block」。
涉及文件
wp-content/themes/fictional-block-theme/functions.php(修改,新增JSXBlock类)wp-content/themes/fictional-block-theme/our-blocks/genericheading.js(新建)wp-content/themes/fictional-block-theme/our-blocks/banner.js(修改)wp-content/themes/fictional-block-theme/package.json(修改,start脚本新增入口)
代码实现
functions.php:新建可复用的 JSXBlock 类,替换掉原本写死的 bannerBlock() 函数:
class JSXBlock {
function __construct($name) {
$this->name = $name;
add_action('init', [$this, 'onInit']);
}
function onInit() {
wp_register_script($this->name, get_stylesheet_directory_uri() . "/build/{$this->name}.js", array('wp-blocks', 'wp-editor'));
register_block_type("ourblocktheme/{$this->name}", array(
'editor_script' => $this->name
));
}
}
new JSXBlock('banner');
new JSXBlock('genericheading');
our-blocks/genericheading.js(新建,先用占位内容测试链路):
import { registerBlockType } from "@wordpress/blocks"
registerBlockType("ourblocktheme/genericheading", {
title: "Generic Heading",
edit: EditComponent,
save: SaveComponent
})
function EditComponent() {
return <div>Hello</div>
}
function SaveComponent() {
return <div>This is our heading block.</div>
}
our-blocks/banner.js:allowedBlocks 收紧成只允许嵌套自己的 genericheading,顺便把 wp.blocks.registerBlockType 换成 import 写法:
import { InnerBlocks } from "@wordpress/block-editor"
import { registerBlockType } from "@wordpress/blocks"
registerBlockType("ourblocktheme/banner", {
title: "Banner",
edit: EditComponent,
save: SaveComponent
})
function EditComponent() {
// ...useMeLater 不变...
return (
<div className="page-banner">
<div className="page-banner__bg-image" style={{ backgroundImage: "url('/wp-content/themes/fictional-block-theme/images/library-hero.jpg')" }}></div>
<div className="page-banner__content container t-center c-white">
<InnerBlocks allowedBlocks={["ourblocktheme/genericheading"]} />
</div>
</div>
)
}
package.json:start 脚本新增第三个入口文件:
"start": "wp-scripts start src/index.js our-blocks/banner.js our-blocks/genericheading.js"
关键改动点:
JSXBlock类的设计:构造函数只接收一个参数$name,存成$this->name属性,再用add_action('init', [$this, 'onInit'])注册初始化钩子——onInit()方法里原本注册脚本/注册 Block 类型这两行代码,把所有写死的字符串(脚本 handle 名、JS 文件路径、Block 命名空间+名字)全部换成$this->name动态拼出来,这样同一个类可以服务任意数量的 Block- PHP 双引号字符串里用
{$this->name}而不是直接$this->name:在双引号字符串内部插值一个对象属性访问表达式时,PHP 要求用花括号{}包起来("{$this->name}.js"),否则 PHP 解析器可能无法正确识别这是「访问属性」还是「字符串里的普通文本」 add_action的第二个参数正确写法是[$this, '方法名']:这是 PHP 里「调用某个对象实例的方法」这一类特殊可调用值的标准写法(这门课在插件开发章节反复用过),不能直接把方法名当字符串传(那样 PHP 找不到该往哪个对象上调用这个方法)new JSXBlock('banner')/new JSXBlock('genericheading'):每新增一个 Block,只需要在functions.php底部加一行new JSXBlock('块名')——这一行传入的字符串同时决定了三件事:要去our-blocks/目录找的 JS 文件名、注册的脚本 handle 名、Block 的具体名字(命名空间统一是ourblocktheme)- 注意实际的 Block 名字是
genericheading(全部小写连写,没有连字符也没有下划线)——这是这份代码实际采用的命名风格,跟口头描述的「generic heading」不完全一致,因为它同时要拼进文件名、JS 变量、Block 类型名这几个不同场合,选一个不含特殊符号的连写形式更省心 - 先用最简单的占位内容验证链路通不通:
genericheading.js的edit/save先分别返回<div>Hello</div>/<div>This is our heading block.</div>,确认这个新 Block 真的能在编辑器里被搜到、插入、保存,之后(下一讲)才会填充真正的富文本编辑逻辑 allowedBlocks从「允许核心 Block」收紧成「只允许自己的genericheading」:<InnerBlocks allowedBlocks={["ourblocktheme/genericheading"]} />——这样在 Banner 内部只能插入这一种自定义标题 Block,不再允许核心的段落/标题/列表- 顺手把
wp.blocks.registerBlockType(...)换成import { registerBlockType } from "@wordpress/blocks"后直接用registerBlockType(...):纯粹是写法上更接近标准 ES6 模块引入的风格(跟 EP182 引入InnerBlocks的方式一致),效果完全相同,banner.js和genericheading.js都做了这个调整 package.json的start脚本要把新的our-blocks/genericheading.js也列成一个入口,否则@wordpress/scripts不会去编译这个新文件,浏览器加载不到对应的build/genericheading.js
Hook / Function 速查
| 名称 | 类型 | 用途 |
|---|---|---|
JSXBlock 类(自定义) | 可复用 PHP 类 | 用一个 $name 参数批量注册「用 JS/JSX 驱动的自定义 Block」,避免每个 Block 重复写注册样板代码 |
get_stylesheet_directory_uri() | WP 内建 function | 获取当前主题目录 URL,用来拼资源文件路径 |
常见坑
- PHP 双引号字符串里直接写
$this->name而不加花括号——解析器可能无法正确识别这是对象属性访问,容易出现空值或报错 add_action第二个参数直接传方法名字符串而不是[$this, '方法名']数组写法——PHP 找不到该调用哪个实例的方法- 新建了 Block 的 JS 文件却忘记同步更新
package.json的start/build脚本入口——这个新 Block 的 JS 永远不会被编译,浏览器加载 404
[截图:Banner Block 内部插入 Generic Heading 占位 Block 后显示"This is our heading block."的编辑器画面]
延伸 / 后续讲座会用到
下一讲要给 genericheading 接上真正的富文本编辑能力,让它既保留 Fictional University 的标题样式,又具备 WordPress 原生富文本字段的加粗/斜体等编辑行为。
Sources
Udemy:
- Become a WordPress Developer: Unlocking Power With Code — Section 28, EP183