为什么需要自定义 Gutenberg 块
WordPress 5.0 引入的 Gutenberg 编辑器(现称为 WordPress 块编辑器)彻底改变了内容创建的方式。它将传统编辑器中的自由文本输入模式,转变为一个基于”块”的结构化编辑体验。虽然 WordPress 内置了丰富的核心块(段落、图像、标题、列表、表格等),但在实际的项目开发中,我们经常会遇到需要定制化内容模块的场景。
例如,一个企业官网需要一个”团队成员展示块”、一个新闻站点需要一个”带图标的统计数字块”、或者一个电商站点需要一个”产品对比表格块”——这些都不是核心块能直接满足的。这时候,自定义 Gutenberg 块开发就显得至关重要。通过创建自定义块,我们不仅能精确控制内容的呈现方式,还能为编辑人员提供直观、友好的编辑体验,大大提升内容管理效率。
本文将从零开始,详细介绍 WordPress Gutenberg 自定义块开发的完整流程,包括环境搭建、块注册、属性定义、编辑器界面构建、数据保存以及前端渲染等核心环节。无论你是 WordPress 主题开发者还是插件开发者,这篇文章都将为你提供系统化的实战指南。

开发环境搭建与项目初始化
在开始 Gutenberg 块开发之前,我们需要搭建一个现代化的 JavaScript 开发环境。WordPress 官方推荐使用
1 | @wordpress/create-block |
工具来快速创建块项目,它会自动配置 Webpack、Babel 等构建工具。
使用 create-block 脚手架工具
首先,确保你的开发环境中安装了 Node.js(推荐 18.x 或更高版本)和 npm。然后通过 npx 运行以下命令创建新的块项目:
1 npx @wordpress/create-block my-custom-blocks cd my-custom-blocks npm start
这个命令会创建一个完整的 WordPress 插件目录结构,包含
1 | src/ |
(源代码目录)、
1 | build/ |
(构建后的生产代码)、
1 | .wp-env.json |
(WordPress 开发环境配置)等。运行
1 | npm start |
后,Webpack 会进入 watch 模式,每次你修改源代码都会自动重新编译。
手动搭建项目结构
如果你希望更灵活地控制构建流程,或者需要在一个插件中注册多个块,可以手动搭建项目结构。下面是一个典型的多块插件项目结构:
1 my-gutenberg-blocks/ ├── my-gutenberg-blocks.php # 主插件文件 ├── package.json # Node.js 依赖管理 ├── webpack.config.js # Webpack 配置 ├── src/ │ ├── blocks/ │ │ ├── team-member/ │ │ │ ├── index.js # 块注册 │ │ │ ├── edit.js # 编辑器界面 │ │ │ ├── save.js # 前端渲染 │ │ │ ├── editor.scss # 编辑器样式 │ │ │ └── style.scss # 前端样式 │ │ └── stats-counter/ │ │ └── ... │ └── index.js # 入口文件 └── build/ # 编译输出
创建好项目结构后,初始化 npm 并安装必要的依赖包:
1 npm init -y npm install @wordpress/scripts --save-dev
1 | @wordpress/scripts |
包封装了 Webpack、Babel、ESLint 等工具的最佳实践配置,大大简化了块开发的构建流程。你只需要在
1 | package.json |
中添加以下脚本即可:
1 "scripts": { "start": "wp-scripts start", "build": "wp-scripts build" }

注册第一个自定义块
注册自定义块是 Gutenberg 开发的第一步。块注册通过 JavaScript 的
1 | registerBlockType |
函数完成,同时需要在 PHP 中注册块资产的钩子函数。
JavaScript 端注册
在
1 | src/blocks/team-member/index.js |
文件中,我们使用
1 | registerBlockType |
注册一个名为
1 | myblocks/team-member |
的自定义块:
1 import { registerBlockType } from '@wordpress/blocks'; import { __ } from '@wordpress/i18n'; import { Edit } from './edit'; import { Save } from './save'; import './editor.scss'; import './style.scss'; registerBlockType('myblocks/team-member', { title: __('团队成员', 'myblocks'), description: __('展示团队成员的头像、姓名、职位和简介', 'myblocks'), category: 'widgets', icon: 'groups', keywords: [__('团队', 'myblocks'), __('成员', 'myblocks'), __('员工', 'myblocks')], attributes: { name: { type: 'string', source: 'html', selector: 'h3' }, position: { type: 'string', source: 'html', selector: '.position' }, bio: { type: 'string', source: 'html', selector: '.bio' }, mediaId: { type: 'number' }, mediaUrl: { type: 'string' }, mediaAlt: { type: 'string', default: '' }, socialLinks: { type: 'array', default: [], items: { type: 'object', properties: { platform: { type: 'string' }, url: { type: 'string' }, }, }, }, }, edit: Edit, save: Save, });
在注册块时,有几个关键参数需要特别注意:
| 参数 | 说明 | 示例值 | ||
|---|---|---|---|---|
|
块在编辑器中的显示名称 | 团队成员 | ||
|
块所在的分类(common/formatting/layout/widgets/embed) | widgets | ||
|
Dashicon 图标名称或 SVG 图标 | groups | ||
|
定义块存储的数据字段及其类型 | 见上方代码 | ||
|
控制对齐、颜色、间距等核心功能的支持 | align, color, spacing |
PHP 端注册脚本和样式
在插件的主 PHP 文件中,我们需要使用
1 | register_block_type |
函数来注册块的脚本和样式资产:
1 <?php /** * Plugin Name: My Gutenberg Blocks */ function myblocks_register_blocks() { // 自动注册 build/blocks 目录下的所有块 $blocks_dir = __DIR__ . '/build/blocks'; if (is_dir($blocks_dir)) { $block_json_files = glob($blocks_dir . '/*/block.json'); foreach ($block_json_files as $block_json) { register_block_type($block_json); } } } add_action('init', 'myblocks_register_blocks');
推荐使用
1 | block.json |
元数据文件来声明块的配置,这样 PHP 端的
1 | register_block_type |
会自动处理脚本和样式的注册与入队:
1 { "apiVersion": 3, "name": "myblocks/team-member", "title": "团队成员", "category": "widgets", "icon": "groups", "description": "展示团队成员信息", "textdomain": "myblocks", "editorScript": "file:./index.js", "editorStyle": "file:./index.css", "style": "file:./style-index.css" }
使用
1 | block.json |
的好处是,WordPress 会自动处理版本控制和依赖管理,你不需要手动调用
1 | wp_register_script |
和
1 | wp_register_style |
。

构建编辑器界面(Edit 组件)
编辑器界面是块最核心的部分之一,它决定了编辑人员在使用块时的体验。Gutenberg 提供了丰富的 React 组件库(
1 | @wordpress/components |
)来构建美观且功能完善的编辑界面。
基础编辑组件实现
下面是一个团队成员块的
1 | Edit |
组件实现,它包含了图片上传、文本编辑和字段分组等常用功能:
1 import { __ } from '@wordpress/i18n'; import { useBlockProps, RichText, MediaUpload, MediaUploadCheck } from '@wordpress/block-editor'; import { Button, TextControl, IconButton } from '@wordpress/components'; export function Edit({ attributes, setAttributes }) { const { name, position, bio, mediaUrl, mediaAlt, socialLinks } = attributes; const blockProps = useBlockProps(); const onSelectImage = (media) => { setAttributes({ mediaId: media.id, mediaUrl: media.url, mediaAlt: media.alt || '', }); }; const addSocialLink = () => { setAttributes({ socialLinks: [...socialLinks, { platform: '', url: '' }], }); }; const updateSocialLink = (index, key, value) => { const newLinks = [...socialLinks]; newLinks[index] = { ...newLinks[index], [key]: value }; setAttributes({ socialLinks: newLinks }); }; const removeSocialLink = (index) => { const newLinks = socialLinks.filter((_, i) => i !== index); setAttributes({ socialLinks: newLinks }); }; return ( <div {...blockProps}> <div className="team-member-card"> <MediaUploadCheck> <MediaUpload onSelect={onSelectImage} allowedTypes={['image']} value={attributes.mediaId} render={({ open }) => ( <Button onClick={open} className="image-uploader"> {mediaUrl ? ( <img src={mediaUrl} alt={mediaAlt} /> ) : ( __('上传头像', 'myblocks') )} </Button> )} /> </MediaUploadCheck> <RichText tagName="h3" value={name} onChange={(val) => setAttributes({ name: val })} placeholder={__('输入姓名...', 'myblocks')} allowedFormats={[]} /> <RichText tagName="p" className="position" value={position} onChange={(val) => setAttributes({ position: val })} placeholder={__('输入职位...', 'myblocks')} /> <RichText tagName="div" className="bio" value={bio} onChange={(val) => setAttributes({ bio: val })} placeholder={__('输入简介...', 'myblocks')} multiline="p" /> <div className="social-links"> <h4>{__('社交链接', 'myblocks')}</h4> {socialLinks.map((link, index) => ( <div key={index} className="social-link-row"> <TextControl value={link.platform} onChange={(val) => updateSocialLink(index, 'platform', val)} placeholder={__('平台', 'myblocks')} /> <TextControl value={link.url} onChange={(val) => updateSocialLink(index, 'url', val)} placeholder={__('URL', 'myblocks')} /> <Button isDestructive onClick={() => removeSocialLink(index)} label={__('删除', 'myblocks')} >×</Button> </div> ))} <Button isSecondary onClick={addSocialLink}> {__('添加社交链接', 'myblocks')} </Button> </div> </div> </div> ); }
使用 InspectorControls 添加侧边栏设置
除了直接在编辑界面中修改内容,我们还可以通过
1 | InspectorControls |
在编辑器右侧的边栏中添加高级设置选项:
1 import { InspectorControls, ColorPalette, PanelColorSettings } from '@wordpress/block-editor'; import { PanelBody, RangeControl, ToggleControl } from '@wordpress/components'; // 在 Edit 组件中添加 <InspectorControls> <PanelBody title={__('显示设置', 'myblocks')}> <ToggleControl label={__('显示边框', 'myblocks')} checked={attributes.showBorder} onChange={(val) => setAttributes({ showBorder: val })} /> <RangeControl label={__('头像尺寸', 'myblocks')} value={attributes.avatarSize || 150} onChange={(val) => setAttributes({ avatarSize: val })} min={50} max={300} /> </PanelBody> <PanelColorSettings title={__('颜色设置', 'myblocks')} colorSettings={[ { value: attributes.cardBgColor, onChange: (val) => setAttributes({ cardBgColor: val }), label: __('卡片背景色', 'myblocks'), }, { value: attributes.textColor, onChange: (val) => setAttributes({ textColor: val }), label: __('文字颜色', 'myblocks'), }, ]} /> </InspectorControls>
通过 InspectorControls 提供的侧边栏设置,你可以让编辑人员在右侧面板中精细调整块的样式和行为,而不影响主编辑区域的内容编辑体验。这是 Gutenberg 块开发中非常重要的用户体验设计模式。

定义块属性与数据存储
属性(attributes)是 Gutenberg 块的核心概念之一,它定义了块存储哪些数据以及数据如何从 HTML 中提取。正确的属性定义直接关系到块的数据完整性和前后端一致性。
属性类型详解
Gutenberg 支持多种属性类型和来源(source),理解这些概念对于编写健壮的块至关重要:
| 类型 | source | selector | 说明 |
|---|---|---|---|
| string | html | .class | 提取元素的 innerHTML |
| string | text | .class | 提取元素的 textContent(无 HTML 标签) |
| string | attribute | img | 提取元素的某个属性值,如 src、alt |
| string | tag | img | 提取元素的标签名 |
| array | query | li | 提取多个元素的匹配结果 |
| number | — | — | 直接存储在块注释中 |
| boolean | — | — | 直接存储在块注释中 |
| object | — | — | JSON 序列化后存储在块注释中 |
1 attributes: { // 从 HTML 标签提取(富文本内容) heading: { type: 'string', source: 'html', selector: 'h2' }, // 从属性提取(如链接 URL) linkUrl: { type: 'string', source: 'attribute', selector: 'a', attribute: 'href' }, // 纯文本(无 HTML) plainTitle: { type: 'string', source: 'text', selector: '.title' }, // 直接存储在块注释中(非 HTML 数据) columns: { type: 'number', default: 3 }, isHighlighted: { type: 'boolean', default: false }, settings: { type: 'object', default: {} }, // 从多个元素查询(列表) items: { type: 'array', source: 'query', selector: 'li', query: { text: { type: 'string', source: 'text' }, link: { type: 'string', source: 'attribute', attribute: 'href' }, }, }, }
动态块与静态块的选择
Gutenberg 块分为静态块和动态块两种类型,选择合适的类型对性能和灵活性有重要影响:
静态块:数据直接以 HTML 形式保存在文章内容中,前后端渲染一致。适合内容结构简单、变化不频繁的场景。静态块的优点是性能好——不需要每次请求都执行 PHP 渲染逻辑。
动态块:在
1 | block.json |
中设置
1 | "render_callback": "myblocks_render_team_member" |
,前端的 HTML 由 PHP 函数动态生成。适合需要根据上下文变化、或依赖实时数据的场景(如最新文章列表)。
一个实用的原则是:如果能用静态块满足需求,就用静态块。只有当需要动态数据或复杂的业务逻辑时才使用动态块。很多开发者过度使用动态块,导致不必要的服务器负载。
1 // block.json 中声明动态块 { "apiVersion": 3, "name": "myblocks/latest-posts", "title": "最新文章", "render_callback": "myblocks_render_latest_posts" } // PHP 渲染回调 function myblocks_render_latest_posts($attributes, $content) { $posts = get_posts([ 'numberposts' => $attributes['count'] ?? 5, 'category' => $attributes['categoryId'] ?? 0, ]); if (empty($posts)) { return '<p>暂无文章</p>'; } $output = '<ul class="wp-block-myblocks-latest-posts">'; foreach ($posts as $post) { $output .= sprintf( '<li><a href="%s">%s</a></li>', esc_url(get_permalink($post)), esc_html($post->post_title) ); } $output .= '</ul>'; return $output; }

前端渲染与样式设计
块的 Save 组件决定了内容在前端的呈现方式。与 Edit 组件不同,Save 组件不能使用状态管理或事件处理——它只负责生成最终的静态 HTML。
Save 组件的实现
1 import { useBlockProps, RichText } from '@wordpress/block-editor'; export function Save({ attributes }) { const { name, position, bio, mediaUrl, mediaAlt, socialLinks, showBorder, avatarSize } = attributes; const blockProps = useBlockProps.save({ className: showBorder ? 'has-border' : '', style: { backgroundColor: attributes.cardBgColor || undefined, color: attributes.textColor || undefined, }, }); return ( <div {...blockProps}> <div className="team-member-card"> {mediaUrl && ( <div className="avatar-wrapper"> <img src={mediaUrl} alt={mediaAlt} width={avatarSize || 150} height={avatarSize || 150} style={{ borderRadius: '50%', objectFit: 'cover' }} /> </div> )} {name && ( <RichText.Content tagName="h3" value={name} /> )} {position && ( <RichText.Content tagName="p" className="position" value={position} /> )} {bio && ( <RichText.Content tagName="div" className="bio" value={bio} /> )} {socialLinks.length > 0 && ( <div className="social-links"> {socialLinks.map((link, index) => ( link.url && ( <a key={index} href={link.url} target="_blank" rel="noopener noreferrer" className={`social-icon social-${link.platform.toLowerCase()}`} > {link.platform} </a> ) ))} </div> )} </div> </div> ); }
样式管理最佳实践
Gutenberg 块的样式分为编辑器和前端两套,需要分别管理:
1 /* style.scss - 同时应用于编辑器和前端 */ .wp-block-myblocks-team-member { .team-member-card { text-align: center; padding: 2rem; border-radius: 8px; background: #fff; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); &.has-border { border: 2px solid #e0e0e0; } } .avatar-wrapper { margin-bottom: 1rem; img { display: inline-block; border-radius: 50%; object-fit: cover; } } .position { color: #666; font-size: 0.9rem; margin-bottom: 1rem; } .social-links { margin-top: 1rem; display: flex; justify-content: center; gap: 0.5rem; } .social-icon { display: inline-block; padding: 0.4rem 0.8rem; border-radius: 4px; background: #f0f0f0; text-decoration: none; font-size: 0.85rem; transition: background 0.2s; &:hover { background: #e0e0e0; } } } /* editor.scss - 仅应用于编辑器 */ .wp-block-myblocks-team-member { .image-uploader { display: block; width: 150px; height: 150px; border-radius: 50%; border: 2px dashed #ccc; cursor: pointer; margin: 0 auto 1rem; overflow: hidden; img { width: 100%; height: 100%; object-fit: cover; } } .social-link-row { display: flex; gap: 0.5rem; margin-bottom: 0.5rem; align-items: end; } }
在样式文件中,所有规则都应该加上块的命名空间前缀(如
1 | .wp-block-myblocks-team-member |
),以避免样式泄漏到其他组件中。这是 Gutenberg 块开发中非常重要的命名空间约定。

高级功能:区块变化与嵌套块
当你的块开发进入进阶阶段,区块变化(Transforms)和嵌套块(InnerBlocks)是必须掌握的两个高级功能。
实现区块变化(Transforms)
区块变化允许用户从一种块类型转换到另一种,例如将核心”段落”块转换为自定义”团队成员”块。这能显著提升编辑体验:
1 import { createBlock } from '@wordpress/blocks'; transforms: { from: [ { type: 'block', blocks: ['core/paragraph'], transform: (attributes) => { return createBlock('myblocks/team-member', { bio: attributes.content, }); }, }, { type: 'prefix', prefix: '/team', transform: (content) => { return createBlock('myblocks/team-member', { name: content.replace('/team', '').trim(), }); }, }, ], to: [ { type: 'block', blocks: ['core/columns'], transform: (attributes) => { return createBlock('core/columns', {}, [ createBlock('core/column', {}, [ createBlock('core/paragraph', { content: attributes.bio, }), ]), ]); }, }, ], },
使用 InnerBlocks 实现嵌套块
InnerBlocks 是 Gutenberg 最强大的功能之一,它允许在块内部嵌套其他块,从而实现复杂的布局结构。例如,一个”团队成员组”块可以嵌套多个”团队成员”子块:
1 import { InnerBlocks, useBlockProps } from '@wordpress/block-editor'; const ALLOWED_BLOCKS = ['myblocks/team-member']; const TEMPLATE = [ ['myblocks/team-member', { name: '张三' }], ['myblocks/team-member', { name: '李四' }], ['myblocks/team-member', { name: '王五' }], ]; // Edit 组件 export function Edit({ attributes }) { const blockProps = useBlockProps({ className: 'team-member-group' }); return ( <div {...blockProps}> <div className="team-grid"> <InnerBlocks allowedBlocks={ALLOWED_BLOCKS} template={TEMPLATE} templateLock="all" orientation="horizontal" /> </div> </div> ); } // Save 组件 export function Save({ attributes }) { const blockProps = useBlockProps.save({ className: 'team-member-group' }); return ( <div {...blockProps}> <div className="team-grid"> <InnerBlocks.Content /> </div> </div> ); }
InnerBlocks 常用的配置选项包括:
1 | allowedBlocks |
(限制可嵌套的块类型)、
1 | template |
(预置的块模板)、
1 | templateLock |
(锁定模板结构)、
1 | orientation |
(排列方向)等。合理使用这些选项可以创建出类似页面构建器的高度可控的编辑体验。
性能优化与调试技巧
Gutenberg 块开发中的性能问题往往被忽视,但在生产环境中却至关重要。以下是一些经过验证的性能优化策略:
减少不必要的重新渲染
React 组件的性能优化同样适用于 Gutenberg 块。使用
1 | React.memo |
和
1 | useCallback |
可以有效减少不必要的重新渲染:
1 import { memo, useCallback } from '@wordpress/element'; const ImageSelector = memo(({ mediaUrl, onSelect }) => ( <MediaUploadCheck> <MediaUpload onSelect={onSelect} allowedTypes={['image']} render={({ open }) => ( <Button onClick={open}> {mediaUrl ? <img src={mediaUrl} /> : '上传'} </Button> )} /> </MediaUploadCheck> )); // 在 Edit 组件中使用 const handleSelectImage = useCallback((media) => { setAttributes({ mediaId: media.id, mediaUrl: media.url }); }, []);
使用 useSelect 高效获取数据
当块需要从 WordPress 核心存储中获取数据时,使用
1 | useSelect |
比直接在组件中请求数据更高效:
1 import { useSelect } from '@wordpress/data'; import { store as coreStore } from '@wordpress/core-data'; function LatestPostsDisplay({ count }) { const posts = useSelect( (select) => { return select(coreStore).getEntityRecords('postType', 'post', { per_page: count || 5, _embed: true, }); }, [count] ); if (!posts) { return <Spinner />; } return ( <ul> {posts.map((post) => ( <li key={post.id}>{post.title.rendered}</li> ))} </ul> ); }
调试工具与常用方法
开发过程中,以下几个调试方法能帮助你快速定位问题:
- 使用 React DevTools:检查组件的 props 和 state,定位不期望的重新渲染
- 检查块注释数据:在代码编辑器中查看文章的 HTML,确认块注释中的数据是否正确保存
- 验证 Save 和 Edit 的一致性:Gutenberg 会检查 Edit 输出的 HTML 结构是否和 Save 输出的匹配,不匹配会导致
1块内容已损坏
警告
- 使用 WP_DEBUG:在
1wp-config.php
中启用
1define('WP_DEBUG', true)查看 PHP 端的错误信息
- 块验证工具:WordPress 提供了
1validateBlock
函数用于在开发过程中验证块数据的完整性
还有一个常被忽略的调试技巧:在编辑器中打开开发者控制台,查看
1 | wp.data.select('core/block-editor').getBlocks() |
可以获取当前编辑器中所有块的状态数据,这对于理解块的数据结构非常有帮助。

总结与最佳实践
本文从零开始系统介绍了 WordPress Gutenberg 自定义块开发的完整流程。从环境搭建到块注册,从编辑器界面到前端渲染,从基础属性定义到高级的嵌套块和区块变化功能,涵盖了块开发的核心知识点。
在实际项目中应用 Gutenberg 块开发时,以下几点最佳实践值得牢记于心:
- 优先使用 block.json:利用
1block.json
元数据文件统一管理块的配置、脚本和样式,让 WordPress 自动处理依赖管理
- 合理划分属性来源:根据数据类型选择合适的
1source
——富文本用 html、纯文本用 text、属性值用 attribute
- Edit 和 Save 保持一致性:两个组件的 HTML 输出结构必须一致,否则 Gutenberg 会抛出内容损坏警告,严重时会导致数据丢失
- 注意国际化:所有用户可见的文本都应该使用
1__()
函数包裹,便于后续的多语言支持
- 渐进增强用户体验:通过 InspectorControls 在侧边栏提供进阶设置选项,而不是在编辑界面堆砌所有控制组件
- 性能优先:合理使用 React.memo、useCallback 和 useSelect 优化性能,避免在编辑器中出现卡顿
Gutenberg 块生态还在持续进化,WordPress 6.x 系列引入了更多的块 API 改进。建议持续关注 WordPress 官方开发者博客和 Block Editor Handbook 获取最新的开发实践。掌握了自定义块开发能力后,你将能够为用户打造专属的、功能强大的编辑体验,让 WordPress 真正成为一个可以随心所欲的内容管理平台。
如果你在学习过程中遇到任何问题,可以查阅 WordPress 官方文档,或者在 GitHub 上搜索开源的自定义块项目来参考学习。实践是最好的学习方式——打开编辑器开始你的第一个自定义块吧。
汤不热吧