Gutenberg 区块开发
这一课你将能做到
- 用官方脚手架 create-block 生成、构建并在编辑器里插入一个自定义区块
- 读懂 block.json 的关键字段,为区块声明 attributes 并在编辑态读写
- 把区块改造成「动态区块」,用 render.php 在服务端实时输出内容(如最新文章列表)
为什么要学
WP 6.x 的编辑器整个建立在 Gutenberg 之上:文章、页面、主题模板都是区块的排列组合。会写区块,你就从「用别人插件的人」变成「造组件的人」;而且区块的 edit 组件是 React 写的——本站 java-nav 前端同款技术栈,学习成本直接复用。
人话:区块 = 编辑器里的 React 组件 + 前台的渲染输出。你在一个项目里同时练 React 和 PHP,这笔投资只赚不亏。
先建立图像
一个区块在两个世界里的样子:
编辑器(React 世界) 前台(PHP/HTML 世界)
┌─────────────────────┐ ┌──────────────────────┐
│ edit.js 编辑态 UI │ 保存 │ 静态区块: save.js 生成的│
│ attributes (状态) │ ─────→ │ HTML 存进 post_content │
└─────────────────────┘ │ 动态区块: render.php │
│ 服务端实时渲染 │
└──────────────────────┘
post_content 里的区块长这样:
<!-- wp:qrocmq/course-card {"level":"进阶"} /-->
静态区块把 HTML「焊死」进内容,动态区块只存锚点注释 + 属性,前台每次请求由 PHP 现渲染——要展示会变的数据,选动态。
跟着做
步骤 1:脚手架生成区块
前置:Node.js 20+。在插件开发目录执行:
npx @wordpress/create-block qrocmq-course-card --namespace=qrocmq
cd qrocmq-course-card
npm start # 开发模式,热更新
启用生成的插件(或 wp plugin activate qrocmq-course-card),新建一篇文章,插入「Course Card」区块,输入文字,保存并预览——第一版已经能跑。
步骤 2:读懂 block.json
打开 src/block.json,核心字段:
{
"$schema": "https://schemas.wp.org/trunk/block.json",
"apiVersion": 3,
"name": "qrocmq/course-card",
"version": "1.0.0",
"title": "Course Card",
"category": "widgets",
"icon": "welcome-learn-more",
"description": "课程卡片:标题 + 难度",
"attributes": {
"title": { "type": "string", "default": "课程标题" },
"level": { "type": "string", "default": "入门" }
},
"supports": {
"html": false,
"align": [ "wide", "full" ],
"color": { "text": true, "background": true }
},
"textdomain": "qrocmq-course-card",
"editorScript": "file:./index.js",
"editorStyle": "file:./editor.css",
"style": "file:./style.css"
}
name 规则:命名空间/小写连字符,全局唯一,不可改。
步骤 3:编辑态加一个难度选择
改 src/edit.js,用 RichText 编辑标题、用 Select 切难度:
import { useBlockProps, RichText } from '@wordpress/block-editor';
export default function Edit( { attributes, setAttributes } ) {
const { title, level } = attributes;
return (
<div { ...useBlockProps() }>
<RichText
tagName="h3"
value={ title }
onChange={ ( v ) => setAttributes( { title: v } ) }
placeholder="课程标题"
/>
<select
value={ level }
onChange={ ( e ) => setAttributes( { level: e.target.value } ) }
>
<option value="入门">入门</option>
<option value="进阶">进阶</option>
</select>
</div>
);
}
保存刷新编辑器:属性变化即时生效,这就是 attributes 的读写闭环。
步骤 4:改成动态区块
动态区块不需要 save.js。把 src/save.js 改为:
export default function save() {
return null; // 交给服务端 render.php
}
在主入口 src/index.js 的 registerBlockType 所在文件旁,让 PHP 端注册渲染回调。新建 qrocmq-course-card.php(插件主文件已存在则在其中加):
<?php
/**
* Plugin Name: QROCMQ Course Card
* Requires PHP: 8.2
*/
add_action( 'init', function () {
register_block_type( __DIR__ . '/build', array(
'render_callback' => function ( $attributes ) {
$recent = get_posts( array(
'numberposts' => 3,
'post_status' => 'publish',
) );
$items = '';
foreach ( $recent as $p ) {
$items .= sprintf(
'<li><a href="%s">%s</a></li>',
esc_url( get_permalink( $p ) ),
esc_html( get_the_title( $p ) )
);
}
return sprintf(
'<div %1$s><h3>%2$s</h3><ul>%3$s</ul></div>',
get_block_wrapper_attributes(),
esc_html( $attributes['title'] ?? '' ),
$items
);
},
) );
} );
npm run build 后,前台插入该区块的位置会实时输出最新 3 篇文章标题。
步骤 5:看产物
构建输出在 build/(block.json + index.js + css)。注意:线上部署的是 build 目录,src 只是源码。
讲清楚要点
- 注册是双向的:
register_block_type()读 build 目录里的 block.json(服务端),编辑器端由registerBlockType()(index.js)接上——两边靠 name 对齐。 - attributes 存在哪:序列化进 post_content 的注释里(如
{"level":"进阶"}),随文章一起入 MySQL。所以属性默认不必声明source,整包存 JSON 最稳。 - 静态 vs 动态的选型标准:纯展示且编辑时确定 → 静态(save 输出 HTML);内容依赖数据库或需要实时计算 → 动态(render_callback)。课程卡片拉课程列表,必须动态。
- useBlockProps / get_block_wrapper_attributes 成对出现:编辑态 React 挂 class(对齐、颜色),服务端输出同样包装,保证前后台样式一致。
- @wordpress/scripts 帮你管好工具链:webpack/esbuild、ESLint、热更新全内置,你只写 src。
- supports 是免费的能力开关:
align、color、spacing打开后,工具栏自动出现对应控件,样式自动注入——不要手写这些轮子。
你可能会懵的地方
-
为什么前台没内容? 动态区块 save 返回 null 后,前台全靠 render_callback;忘了
npm run build或 build 目录里没有 block.json,都会导致渲染失败。 -
RichText 和普通 input 有什么区别? RichText 保留行内格式(加粗、链接)并与区块属性同步;input 只存纯文本。编辑标题用 RichText 是惯例。
-
attributes 里的 source 是干什么的? 声明「从保存的 HTML 里提取属性」(如 source: 'children')。新版本推荐不写 source,让整包属性存进注释,避免迁移地狱。
-
编辑器里报「此区块遇到错误」? 区块 JS 运行时异常。F12 看 Console 红字,多为 import 路径、未 build 或 JSX 语法错。
-
区块和短代码(shortcode)怎么选? 有编辑态 UI、可视化预览需求选区块;纯服务端文本替换、要嵌在经典编辑器里,短代码仍可用,但新项目默认区块。
错法对照
| 错法 | 问题 | 正解 |
|---|---|---|
| save 输出 HTML 且又有 render_callback | 前台内容重复 | 动态区块 save 返回 null |
name 写成 Qrocmq/CourseCard | 注册失败 | qrocmq/course-card 全小写 |
| 改了 src 直接看效果 | 编辑器跑的是 build 产物 | 开发期 npm start,上线 npm run build |
| render_callback 里不转义输出 | XSS 漏洞 | esc_html / esc_url(第 10 课展开) |
| 漏掉 get_block_wrapper_attributes() | 对齐与颜色 class 丢失 | 输出根元素时始终包裹 |
卡点急救
- 区块插入后编辑器报错:Console 定位;确认
npm start进程存活、build/ 与 src 同步。 - 前台样式不生效:确认 block.json 的
style/editorStyle路径存在;style.scss 是前后台共用,editor.scss 只进编辑器。 - create-block 下载卡住:切换 npm 源或使用代理;也可以
--template=static生成最小模板减少依赖。
面试常问
-
静态区块和动态区块的区别与选型? 静态在保存时把 HTML 写入 post_content,渲染零成本;动态只存属性、由服务端 render_callback 实时渲染,适合展示数据库内容或需保持模板逻辑同步的场景。
-
block.json 的作用? 区块的元数据与注册清单:name、attributes、supports、脚本样式入口,服务端与编辑器共享同一份定义,是 6.x 区块规范的核心文件。
-
区块的属性数据最终存在哪? 序列化在文章 post_content 的区块注释中(JSON 形式),随 wp_posts 表存储;动态区块前台的 HTML 不落库,每次请求渲染。
自测三问
- 动态区块为什么把 save.js 返回 null?
- 写出 register_block_type 读构建目录的最小代码。
- supports 里开启 align 与 color 后,分别带来什么自动能力?
小结一句话
区块 = block.json 声明 + React 编辑态 + save 或 render_callback 输出;静态存 HTML、动态跑 PHP,会写一个课程卡片区块,就打通了 WP 6.x 内容组件化的任督二脉。
延伸
- Create a Block 官方教程
- block.json 元数据参考
- 下一课:插件开发与 Hook 体系——让 action/filter 接管 WP 的执行流程。