Gutenberg 块类型更新传播实战指南:Block、Pattern 与模板部件的维护策略
【免费下载链接】gutenbergThe Block Editor project for WordPress and beyond. Plugin is available from the official repository.项目地址: https://gitcode.com/GitHub_Trending/gu/gutenberg
本篇指南围绕 Gutenberg 项目官方文档 docs/how-to-guides/propagating-updates.md 展开,聚焦 WordPress 块主题开发中的核心痛点:如何在模板、模式(Pattern)或块级别向整个站点传播更新。读者将掌握各类内容类型(块、模式、同步模式、模板部件与模板)的同步机制与限制,学会在创建内容之前就制定正确的更新策略,并结合仓库源码理解
render_callback、块弃用(Deprecation)等底层原理,从而显著降低未来的维护成本。
一、更新传播的核心原则:先规划,再创建
在深入各类内容类型之前,需要先建立两个顶层认知——它们决定了后续所有维护工作的难度。
1.1 尽早确定"哪些内容需要更新"
并非所有内容都能在全站范围内被统一更新,内容的创建方式直接决定了后续更新的可能性。因此,务必在创建内容之前,花时间判断哪些内容在未来需要被更新,并把它们放入合适的格式中。
例如:
- 期望随主题版本更新的内容,应该放进模板(Templates)或模板部件(Template Parts);
- 期望独立于主题更新、随站点数据持久存在的内容,应该考虑动态块或同步模式(Synced Patterns);
- 期望在站点间复制、但不随源变化的"示例型"内容,则适合模式(Patterns)。
这一前置决策将极大影响未来的维护成本——在创建阶段多花几分钟,可能节省后续数小时的跨站更新工作。
1.2 在块级别拥抱主题设计
块主题设计(Block Theme Design)要求开发者进行一次思维转变:从过去"设计大块区域、再通过版本更新统一控制"的思路,转向原子级设计。
以块为主题设计的核心单元,通常通过theme.json定制实现。其核心理念是:每个独立的"原子"(即块)都应该可以被移动、编辑、删除并重新组合,而不会导致整个设计崩塌。
// theme.json 中的全局样式定制示意 { "version": 3, "settings": { "color": { "palette": [ { "name": "Primary", "slug": "primary", "color": "#0073aa" } ] } }, "styles": { "blocks": { "core/heading": { "color": { "text": "var:preset|color|primary" } } } } }当你越接近块级别的设计,就越不需要向模式(Patterns)和模板(Templates)传播更新——因为原子部件已经各就各位,它们的布局如何变化并不重要。这也意味着:全局样式的更新(通过theme.json)会自动传播到所有使用了该块的位置,这是块主题相对传统主题的最大优势之一。
二、内容类型详解与各自的更新方式
不同内容类型的同步能力差异巨大。下表先给出概览,随后逐一展开:
| 内容类型 | 是否可跨站点同步更新 | 更新方式 | 适用场景 |
|---|---|---|---|
| 静态块 | 仅通过弃用(Deprecation)手动升级 | 注册deprecated版本 | 结构可能随时间变化的块 |
| 动态块 | 是(服务端渲染) | render_callback | 依赖外部数据的块 |
| 混合块 | 是 | render_callback+save()兜底 | 需要灵活性与渐进增强的块 |
| 模式(Pattern) | 否(插入后即脱离) | 无(仅 CSS 类名变通) | 示例/初始内容 |
| 同步模式(Synced Pattern) | 是 | 全站自动同步 | 需要内容、结构、样式全同步的场景 |
| 模板部件 / 模板 | 部分(用户未编辑时) | 更新主题文件;用户已编辑则需协商 | 站点骨架与布局 |
2.1 块(Blocks):根据块的"天性"选择管理方式
块的更新管理方式取决于块本身的特性,主要有四条路径:
路径一:动态块(Dynamic Blocks)
如果块依赖外部数据(如数据库、API、站点配置),那么从一开始就将其实现为动态块通常是更好的选择——通过render_callback在服务端渲染输出,它能提供更多控制力。
在 Gutenberg 仓库的 PHP 侧,可以看到大量动态块的注册示例。例如 lib/blocks.php 中注册旧的社交链接块时,通过register_block_type()传入render_callback:
register_block_type( 'core/social-link-' . $service, array( 'category' => 'widgets', 'attributes' => array( 'url' => array( 'type' => 'string' ), 'service' => array( 'type' => 'string', 'default' => $service, ), 'label' => array( 'type' => 'string' ), ), 'render_callback' => 'gutenberg_render_block_core_social_link', ) );其中render_callback指向的函数gutenberg_render_block_core_social_link会在每次页面渲染时执行,决定块的实际输出。动态块的每一次内容变更都直接反映在站点前端,无需任何"传播"动作——这正是"依赖外部数据"类块的首选方案。
路径二:静态块 + 弃用(Deprecation)
如果块的结构预期会随时间变化(例如标记从<p>改为<div>),推荐从静态块开始,使用save()方法定义默认输出。当后续版本需要变更标记或属性集时,通过**块弃用(Block Deprecation)**机制为旧内容提供升级路径。
块弃用的工作机制不同于数据库迁移——它不是一条链式执行的更新管道,而是一个"尝试匹配"的过程(详见后文源码解析)。一个块可以定义多个弃用版本,每个版本包含attributes、supports、save,以及可选的migrate和isEligible。
const { registerBlockType } = wp.blocks; registerBlockType( 'gutenberg/block-with-deprecated-version', { attributes: { text: { type: 'string', default: 'some random value' }, }, supports: { className: false }, save( props ) { return <div>{ props.attributes.text }</div>; // 新版本:div }, deprecated: [ { attributes, // 旧属性定义 supports, save( props ) { return <p>{ props.attributes.text }</p>; // 旧版本:p }, }, ], } );路径三:混合块(Hybrid Blocks)
随时间推移,还可以将静态块升级为混合块——同时保留save()的默认输出,并加入render_callback。渲染时以save()的输出作为兜底,同时处理替代输出。这种方式兼具两者优点,但请注意:灵活性与控制力是以渲染期间额外的处理开销为代价的,应在性能敏感的场景下谨慎权衡。
路径四:利用 Create Block 工具
无论选择哪种路径,开始创建块时都可以借助Create Block 工具快速搭建项目骨架,节省大量时间——它是一套官方支持的脚手架,用于生成注册块的 WordPress 插件,自动产出 PHP、JS、CSS 代码与现代构建配置,无需额外配置。仓库中的完整说明与命令示例见 packages/create-block/README.md:
npx @wordpress/create-block@latest todo-list cd todo-list npm start小结:需要全站自动更新且依赖外部数据 → 动态块;结构会演进 → 静态块 + Deprecation;两者兼需 → 混合块。
2.2 模式(Patterns):插入即脱离,无法事后更新
对于希望日后更新的内容,不要使用模式(Patterns)——应改用复用块(Reusable Blocks)或模板部件(Template Parts)。
原因在于:模式一旦被插入站点,就与原始模式完全脱离。可以把模式理解为"示例/样例/初始内容":
- 插入器(Inserter)中展示的模式可能会随时间演变,但这些变更不会自动应用到任何已插入的模式实例;
- 与复用块或模板部件块不同,插入后的模式不会再与源保持任何同步关系。
变通方案:为模式外层块添加类名
如果某个模式带有自定义样式,一个潜在变通方案是为模式的包裹块添加类名。例如,为 Group 块添加themeslug-special类:
<!-- wp:group {"className":"themeslug-special"} --> <div class="wp-block-group themeslug-special"> <!-- 嵌套的模式块 --> </div> <!-- /wp:group -->这个方案并非万无一失,因为用户可以通过编辑器界面修改类名。不过,由于该设置位于"高级(Advanced)"面板下,大多数情况下会保持不变。这为主题作者提供了针对某些模式类型的 CSS 控制能力,允许他们更新既有用途。但它无法阻止用户进行无法被更新的巨大改动。
2.3 同步模式(Synced Patterns):天然同步,但要留意粒度
正如其名,同步模式(Synced Patterns)天然地在整个站点范围内保持同步。但需要牢记当前的局限:当更新发生时,内容、HTML 结构和样式都会一起保持同步,三者是捆绑的。
如果需要的更新比这更精细(例如只更新内容、不动结构,或只更新样式、不动内容),同步模式就不适用了——这时动态块可能是更合适的方案。这一判断同样应在创建内容前完成,因为同步模式一旦被广泛使用,其"全量同步"的特性会把每一次更新都放大到全站。
2.4 模板部件与模板(Template Parts and Templates)
块主题允许用户直接编辑模板和模板部件,因此更新管理必须考虑用户拥有更大访问权限这一现实。核心机制如下:
- 用户未修改文件时:你在文件系统(主题目录下的
templates/与parts/文件夹)中做的修改,会直接反映到用户站点——只需更新文件,用户就能获得变更; - 用户已编辑过模板时:主题更新中的新模板不会自动覆盖用户已编辑的模板。只有新用户或尚未编辑过模板的用户才能看到更新后的模板。
如果用户已经修改了模板,要更新他们的模板只有两条路径:
- 还原(Revert)他们的全部修改;
- 在数据库中更新模板和模板部件。
一般而言,如果用户已经修改了模板,建议保持模板原样,除非与用户达成了明确约定(例如在代理/外包场景中)。
更新模板时的两个警示
- 谨慎更换模板部件的引用:例如
templates/page.html在 v1.0 引用了parts/header.html,若在 v2.0 改为引用parts/header-alt.html,一些开发者可能将其视为"绕过用户已修改 header.html"的变通方案。但这极可能破坏用户的自定义设计——因为page.html不再引用正确的部件,除非用户同时修改并保存了页面模板。 - 不要在主题更新中删除模板部件:用户可能创建了自定义的顶层模板,其中包含对该部件的调用并期望它持续存在。删除部件会导致这些自定义模板失效。
仓库佐证:在 lib/block-template-utils.php 中可以看到 Gutenberg 如何将模板与模板部件作为一等公民导出——站点编辑器导出功能会把get_block_templates()得到的模板写入templates/目录、把wp_template_part类型的模板部件写入parts/目录,并连同theme.json一起打包。这从侧面印证了"模板/部件即主题文件"的设计:只要用户未在数据库中覆盖它们,文件层面的更新即可传播到全站。
三、源码深挖:块更新传播的底层机制
为了让上述策略"知其然且知其所以然",下面深入仓库源码,解析两个关键机制的实现。
3.1 块弃用(Deprecation)的解析与匹配流程
块弃用的核心实现在 packages/blocks/src/api/parser/apply-block-deprecated-versions.ts。其工作流程与直觉相反——它不是链式的数据迁移,而是"队列式尝试匹配":
- 解析出的块(Block)若验证无效,则取出块类型上注册的
deprecated定义数组,从头到尾逐一尝试; - 若当前块有效,但某个弃用定义了
isEligible函数且返回true,则该弃用也会被尝试(用于"块技术上仍然有效,但需要更新属性/内部块"的场景); - 每个弃用都会构造一个"弃用块类型"——它不会自动继承当前版本的
attributes、supports、save,因为这些会直接影响解析与序列化。从 packages/blocks/src/api/constants.ts 可以看到弃用对象允许的键:
export const DEPRECATED_ENTRY_KEYS = [ 'attributes', 'supports', 'save', 'migrate', 'isEligible', 'apiVersion', ];- 用弃用定义重新解析块的属性后,通过
validateBlock校验;若仍无效,尝试applyBuiltInValidationFixes内置修复;再无效则跳过该弃用; - 若弃用定义了
migrate函数,则用其将旧属性/内部块转换为新格式(可返回新的属性对象,或[attributes, innerBlocks]元组); - 一旦某个弃用成功产出有效块,该块的属性与内部块会被交给当前版本的
save()重新生成新内容,弃用流程随即停止。
对维护者的启示:
- 弃用数组应按倒序时间排列(最新在前),让编辑器优先尝试最可能成功的版本,避免不必要的开销;
- 若一个块的
save导入了其他文件中的函数,这些文件的变更可能意外改变弃用行为——建议在弃用文件中保存这些函数的快照副本; - 多个弃用之间存在"跳过"机制:某个弃用的
save无效时,它的migrate也不会执行。因此当你需要执行新的迁移(如将内容移入InnerBlocks)时,可能需要在多个弃用中同时更新migrate,才能覆盖所有历史版本。
上述机制的更完整参考文档见 docs/reference-guides/block-api/block-deprecation.md。
3.2 块的注册与render_callback的 PHP 侧实现
块在 JS 侧通过registerBlockType()注册(见 packages/blocks/src/api/registration.ts),其名称必须符合namespace/slug格式(小写字母、数字、连字符)。而动态块的render_callback则是 PHP 侧的关键钩子。
以 lib/blocks.php 中的社交链接块为例,register_block_type()接收的数组中包含render_callback,该回调函数在块渲染时执行。这种"服务端决定输出"的模式意味着:块的更新只需修改回调逻辑本身(或它读取的数据源),全站所有该块实例的渲染结果即刻更新——这是动态块在"更新传播"上天然优于静态块的根本原因。
3.3 模板/部件与theme.json的联动
从 lib/block-template-utils.php 的导出流程可以看到:站点编辑器导出时会读取所有wp_template与wp_template_part条目、移除模板部件块上的主题属性(_remove_theme_attribute_from_template_part_block)、并与用户数据合并后导出theme.json。这说明:
- 模板与部件在块主题中是文件与数据库并存的实体:主题文件是"出厂状态",用户编辑后的版本存入数据库并优先生效;
theme.json中的全局样式会与用户数据(WP_Theme_JSON_Resolver_Gutenberg::get_user_data())合并,这解释了为什么块级theme.json定制能实现"原子级设计 + 自动传播"——样式定义集中在一处,所有引用该样式的块同步更新。
四、决策速查:创建内容前回答这四个问题
将全文要点浓缩为一张决策清单,供实际项目中快速参考:
- 这块内容会依赖外部数据吗?→ 是,选择动态块(
render_callback)。 - 这块内容的结构会随时间演进吗?→ 是,选择静态块并规划
deprecated弃用版本(可配合migrate迁移属性与内部块)。 - 这是一次性样例内容,还是需要长期同步的内容?→ 一次性样例用模式(Patterns);需要全站同步用同步模式(Synced Patterns)或模板部件(Template Parts);需要细粒度控制则用动态块。
- 用户会直接编辑这部分内容吗?→ 会,则尊重用户的编辑结果——文件更新不会覆盖已编辑的模板/部件;如需强制更新,只能还原用户修改或在数据库中更新,且最好与用户事先达成一致。
五、延伸资源
- 块弃用完整参考(含属性重命名、内部块迁移示例)
- Create Block 工具使用说明(脚手架命令与选项)
- 模式(Pattern)块 API 参考
- 模板/模板部件导出与读取的 PHP 实现
- 弃用机制解析源码
- 块注册 API 源码
- 动态块
render_callback注册示例
需要说明:块弃用、Create Block 工具与模式相关的外部官方文档(developer.wordpress.org 等)不在本仓库范围内,本文以仓库内对应文档与源码为准展开;如需查阅更完整的官方说明,可在对应版本的 WordPress 开发者手册中搜索对应章节标题。
【免费下载链接】gutenbergThe Block Editor project for WordPress and beyond. Plugin is available from the official repository.项目地址: https://gitcode.com/GitHub_Trending/gu/gutenberg
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考