WP DEVELOP

EP183. “用 PHP 类批量注册 Block + 新建 generic-heading”

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

这一章接下来要创建大量自定义 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.jsallowedBlocks 收紧成只允许嵌套自己的 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.jsonstart 脚本新增第三个入口文件

"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.jsedit/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.jsgenericheading.js 都做了这个调整
  • package.jsonstart 脚本要把新的 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.jsonstart/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