EP182. “InnerBlocks 嵌套子区块与 allowedBlocks 限制”
🔒 登录后可标记已读📌 并入 EP181 的提醒:从传统主题复制过来的 functions.php 里有个 pageBanner() 函数,里面用 if (!$args['title']) 这种写法直接判断数组项是否为假值——在较新版本的 PHP 环境下,如果这个键根本不存在,会先触发一次「访问未定义数组键」的警告。要改成先用 isset() 判断键是否存在:if (!isset($args['title'])),subtitle、photo 两处同理。
让 Banner Block 的正文区域(大标题/中标题/小标题/按钮)不再是写死的固定几行 JSX,而是允许在编辑器里像搭积木一样自由增删任意数量、任意顺序的标题和列表——用 WordPress 提供的 InnerBlocks 组件实现「一个 Block 内部可以嵌套其他 Block」。同时用 allowedBlocks 属性限制只能嵌套段落、标题、列表这三种类型,避免用户塞入完全不搭调的内容(比如图片轮播、视频嵌入等)。这一讲是为下一讲做铺垫——目前先允许嵌套 WordPress 核心 Block,下一讲会替换成只允许嵌套自己写的专属 Block 类型。
涉及文件
wp-content/themes/fictional-block-theme/functions.php(修改,isset()勘误)wp-content/themes/fictional-block-theme/our-blocks/banner.js(修改)
代码实现
functions.php:pageBanner() 函数里补上 isset() 判断:
if (!isset($args['title'])) { /* ... */ }
if (!isset($args['subtitle'])) { /* ... */ }
if (!isset($args['photo'])) { /* ... */ }
our-blocks/banner.js(完整文件):
import { InnerBlocks } from "@wordpress/block-editor"
wp.blocks.registerBlockType("ourblocktheme/banner", {
title: "Banner",
edit: EditComponent,
save: SaveComponent
})
function EditComponent() {
const useMeLater = (
<>
<h1 className="headline headline--large">Welcome!</h1>
<h2 className="headline headline--medium">We think you’ll like it here.</h2>
<h3 className="headline headline--small">
Why don’t you check out the <strong>major</strong> you’re interested in?
</h3>
<a href="#" className="btn btn--large btn--blue">
Find Your Major
</a>
</>
)
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={["core/paragraph", "core/heading", "core/list"]} />
</div>
</div>
)
}
function SaveComponent() {
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.Content />
</div>
</div>
)
}
关键改动点:
- 原本的问题:之前
EditComponent/SaveComponent里的大标题/中标题/小标题/按钮都是写死的固定几行 JSX——如果以后想要「两个中标题 + 两个按钮」或者任意其他排列组合,这套写死的结构完全没法适应 InnerBlocks是什么:@wordpress/block-editor提供的组件,把它放进一个自定义 Block 的edit函数里,编辑器就会在这个位置显示一个「点击加号即可插入子 Block」的区域,效果上等于让这个自定义 Block 变成了一个可以自由容纳其他 Block 的容器import { InnerBlocks } from "@wordpress/block-editor":虽然InnerBlocks本来就能通过浏览器全局wp.blockEditor.InnerBlocks直接访问,但配合@wordpress/scripts的 Webpack 配置,可以像这样用标准import语法写,效果上是一样的(Webpack 会自动识别这类包名、转成读取全局对象),只是写法更符合大家熟悉的 ES6 模块习惯- 把原本写死的 JSX 存进一个没有被使用的变量
useMeLater(用 React Fragment<>...</>包起来)——纯粹是留着当参考草稿,方便以后要恢复类似内容时能直接复制,不代表这段代码本讲会真正生效 <InnerBlocks allowedBlocks={[...]} />:allowedBlocks是一个字符串数组,只列出允许被嵌套进来的 Block 类型名——这里限制成"core/paragraph"、"core/heading"、"core/list",用户在编辑器里插入子 Block 时,可选列表里只会出现这三种,其余核心 Block(图片、按钮、视频等)都不会出现在候选里SaveComponent()要同步改成真正输出内容,而不是继续用占位文字:外层的<div className="page-banner">等包装结构跟EditComponent()保持一致,唯一的区别是内部不再用<InnerBlocks />(这是编辑状态下「可以继续添加内容」的交互组件),而是用<InnerBlocks.Content />——这是InnerBlocks提供的一个静态展示版本,负责把用户在编辑器里实际添加的所有子 Block 内容原样渲染/保存出来- 验证方式:直接看数据库——保存模板后,
wp_posts.post_content字段里能看到完整的嵌套结构:外层是<!-- wp:ourblocktheme/banner -->,内部嵌套着用户实际添加的子 Block(比如<!-- wp:paragraph -->),证明InnerBlocks/InnerBlocks.Content这一对组件确实在负责「捕获子 Block 内容」和「渲染保存子 Block 内容」这两件事 allowedBlocks目前限制的是 WordPress 核心 Block,属于阶段性过渡——下一讲会创建两个全新的自定义 Block 类型(ourblocktheme/generic-heading、ourblocktheme/generic-button之类的命名思路),把allowedBlocks换成只允许这些自己写的专属类型嵌套进来,这样才能精确控制「嵌套进来的标题到底该长什么样、输出什么 HTML」
Hook / Function 速查
| 名称 | 类型 | 用途 |
|---|---|---|
InnerBlocks(@wordpress/block-editor) | React 组件 | 在自定义 Block 的 edit 函数里提供「可以嵌套添加其他 Block」的可编辑区域 |
InnerBlocks.Content | React 组件 | 在自定义 Block 的 save 函数里,输出用户实际添加的所有子 Block 内容 |
allowedBlocks(InnerBlocks 的 prop) | 配置项 | 字符串数组,限制这个容器内允许插入哪些 Block 类型 |
常见坑
- 只在
edit里用<InnerBlocks />,save里却继续用旧的占位文字或者写死的 JSX——用户实际添加的子 Block 内容不会被正确保存,前台/数据库看不到真正的嵌套内容 - 忘记
save函数要用InnerBlocks.Content(不是InnerBlocks本身)——两者用途不同,一个是可编辑区域,一个是只读渲染 allowedBlocks的字符串拼错(比如少了core/前缀)——对应的 Block 类型不会出现在允许插入的候选列表里
[截图:编辑器里 Banner Block 内部点击加号插入子 Block 时,候选列表只显示段落/标题/列表三种类型]
延伸 / 后续讲座会用到
下一讲要创建两个全新的自定义 Block 类型(标题、按钮),替换掉这里暂时允许的 WordPress 核心 Block,实现完全自主可控的 HTML 输出。
Sources
Udemy:
- Become a WordPress Developer: Unlocking Power With Code — Section 28, EP181, EP182