WP DEVELOP

EP182. “InnerBlocks 嵌套子区块与 allowedBlocks 限制”

首页 WordPress 开发课程 BLOCK THEME(2024 最佳实践) · EP182
约 15 分钟· #EP182#BLOCK THEME(2024 最佳实践)
🔒 登录后可标记已读

📌 并入 EP181 的提醒:从传统主题复制过来的 functions.php 里有个 pageBanner() 函数,里面用 if (!$args['title']) 这种写法直接判断数组项是否为假值——在较新版本的 PHP 环境下,如果这个键根本不存在,会先触发一次「访问未定义数组键」的警告。要改成先用 isset() 判断键是否存在:if (!isset($args['title']))subtitlephoto 两处同理。


让 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.phppageBanner() 函数里补上 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&rsquo;ll like it here.</h2>
      <h3 className="headline headline--small">
        Why don&rsquo;t you check out the <strong>major</strong> you&rsquo;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-headingourblocktheme/generic-button 之类的命名思路),把 allowedBlocks 换成只允许这些自己写的专属类型嵌套进来,这样才能精确控制「嵌套进来的标题到底该长什么样、输出什么 HTML」

Hook / Function 速查

名称类型用途
InnerBlocks@wordpress/block-editorReact 组件在自定义 Block 的 edit 函数里提供「可以嵌套添加其他 Block」的可编辑区域
InnerBlocks.ContentReact 组件在自定义 Block 的 save 函数里,输出用户实际添加的所有子 Block 内容
allowedBlocksInnerBlocks 的 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