块深度解析:从 block.json 到编辑渲染的完整实现)
Gutenberg Custom HTMLcore/html块深度解析从 block.json 到编辑渲染的完整实现【免费下载链接】gutenbergThe Block Editor project for WordPress and beyond. Plugin is available from the official repository.项目地址: https://gitcode.com/GitHub_Trending/gu/gutenberg导读本文基于 Gutenberg 插件仓库中 Custom HTML 块的官方文档 及其完整源码实现深入剖析core/html这一核心静态块的元数据定义、属性与支持能力、序列化标记结构以及它的编辑体验、代码转换与权限控制机制。读完本文你将掌握 Custom HTML 块的完整工作方式理解其“静态块 inner content”的存储模型并能据此排查二次开发中遇到的序列化、权限与预览相关问题。一、Custom HTML 块是什么Custom HTMLcore/html是 Gutenberg 内置的“自定义 HTML”块官方定位一句话即可概括添加自定义 HTML 代码并在编辑时实时预览效果Add custom HTML code and preview it as you edit。它属于widgets小工具分类块类型为静态块Static block——这意味着它的标记会以原始 HTML 的形式直接保存在文章内容中而不是像动态块那样由服务端渲染函数在输出时生成。从 block.json 可以看到它的元数据全貌元数据字段值说明namecore/html块的唯一注册名出现在注释分隔符中titleCustom HTML块在编辑器中的显示名称categorywidgets归入小工具分类descriptionAdd custom HTML code and preview it as you edit官方描述apiVersion3使用第 3 版块 APIkeywords[embed]搜索关键词editorStylewp-block-html-editor编辑器专用样式句柄textdomaindefault文本域在 index.js 中块通过initBlock完成注册并配置了图标、示例内容与编辑/保存/转换实现。值得注意的是它的示例内容是一个marquee标签——这是块作者用来展示“可以放入任意 HTML”的经典演示片段。二、属性Attributes唯一的content与role: local语义按照块 API 规范属性通过 block.json 中的attributes字段定义。Custom HTML 块只有一个属性属性类型默认值说明contentstring—Rolelocalcontent的role: local非常关键它表示该属性是编辑器本地状态不会作为 JSON 序列化进块注释分隔符中。这一点在 test/index.jsdom.test.js 中有专门的测试断言it( keeps the content attribute out of the block delimiter, () { const block createBlock( core/html, { content: marqueeHello/marquee, } ); // role: local prevents the attribute from being written into // the comment delimiter as JSON. expect( serialize( block ) ).not.toContain( {content ); } );历史遗留content属性的迁移逻辑从源码注释与 edit.jsx 可以看到一段重要的兼容性设计早期版本的 Custom HTML 块把标记存放在content属性中而现在的实现把标记迁移到了块的inner content内部内容片段中。为了兼容旧的程序化创建方式例如createBlock( core/html, { content } )块在加载时一旦发现attributes.content存在就会通过deprecated()输出废弃警告自 7.1 版本起建议改用 inner content用updateBlock把attributes.content中的标记写入innerContent同时清除content属性。这正是“不丢数据”的渐进式迁移策略旧内容自动升级到新存储模型而不会在版本升级时丢失。三、Supports 能力矩阵为何它如此“克制”Supports 决定了编辑器为该块提供哪些通用能力类名、锚点、可见性开关等。Custom HTML 块的 supports 配置相当克制block.json 中定义如下Supports 项值影响customClassNamefalse不允许用户在额外 CSS 类中自定义类名classNamefalse不自动注入.wp-block-html类htmlfalse不提供“以 HTML 方式编辑”的块级切换interactivity.clientNavigationtrue支持客户端导航在交互式前端路由中保留listViewtrue在列表视图中可见customCSSfalse不提供自定义 CSS 支持visibilityfalse不提供块可见性控制这些“关闭”项是有意为之Custom HTML 块的定位就是把原始 HTML 原样交还给用户如果套上自动类名或自定义 CSS 包装反而会污染用户输入的标记。这种“少即是多”的设计与core/paragraph等富文本块形成鲜明对比。四、块标记Block Markup静态块的序列化模型由于是静态块Custom HTML 的标记会原样写入文章内容以!-- wp:html --与!-- /wp:html --注释分隔符包裹。文档给出了典型示例!-- wp:core/html -- h1Some HTML code/h1 divThis is a div/div !-- /wp:core/html --源码层面的真相save()返回null值得深入说明的是save.js 的实现只有一行// The blocks markup is serialized from its innerContent (static HTML // fragments interleaved with inner blocks), not from a save implementation. export default function save() { return null; }也就是说Custom HTML 块的标记并非由save()函数生成而是直接取自块的innerContent——即“静态 HTML 片段与内部块inner blocks交错排列”的数组。这正是 test/index.jsdom.test.js 中验证的行为当 innerContent 为[div, null, /div]且内嵌一个段落块时序列化结果是!-- wp:html -- div!-- wp:paragraph -- Editable !-- /wp:paragraph --/div !-- /wp:html --这意味着 Custom HTML 块内部可以嵌套其他块——用户在 HTML 代码里书写的!-- wp:* --分隔符会被解析为真实的内部块这些块仍然可编辑而其余部分保持为静态 HTML 片段。五、编辑体验占位符、代码弹窗与实时预览Custom HTML 的编辑界面由 edit.jsx 驱动整体流程分三种状态空内容占位状态当内容为空时显示带“Custom HTML”标签和“Edit HTML”主按钮的Placeholder占位符点击后弹出编辑弹窗HTMLEditModal。正常编辑状态通过InnerContent来自 block-editor 私有 API渲染已解析的标记工具栏BlockControls提供“Edit code”按钮右侧检查器InspectorControls也提供同名的“Edit code”次按钮。弹窗编辑点击任一“Edit code”都会打开 modal.jsx 中的HTMLEditModal。编辑弹窗的三栏式设计编辑弹窗是本块最富特色的部分它在一个大型Modal中提供Tab 页签HTML / CSS / JavaScript 三个页签CSS 与 JavaScript 页签仅在用户拥有 unfiltered HTML 权限时显示分栏布局左侧是带等宽字体、LTR 方向无论语言设置如何HTML 代码始终从左到右书写的代码编辑区右侧是实时预览区窄屏移动端视口自动切换为上下布局全屏切换桌面端提供全屏按钮fullscreen/square图标切换原生撤销通过useNativeUndo保留浏览器自身的撤销/重做行为——因为弹窗内字段的本地状态只在点击“Update”时才提交给块期间的撤销必须交给浏览器底栏按钮Cancel 与 Update点击 Update 提交并关闭弹窗。预览是如何实现的预览组件在 preview.jsx 中实现核心是wordpress/components的SandBox——一个沙箱化的 iframe。它做了两件事注入一组DEFAULT_STYLES清除编辑器可能继承给预览内容的边距/内边距等样式通过transformStyles把编辑器全局样式设置getSettings().styles转换后注入预览 iframe使预览尽可能接近真实前台效果。此外当块未被选中时会渲染一个.block-library-html__preview-overlay透明覆盖层。源码注释解释了原因部分浏览器不会把沙箱 iframe 内的点击事件冒泡出来这个覆盖层保证用户在预览区域点击时能重新选中该块。权限与内容剥离unfiltered HTML 检查编辑弹窗通过settings.__experimentalCanUserUseUnfilteredHTML判断用户是否拥有unfiltered_html权限通常仅管理员/编辑者具备。这一判断直接影响两处行为CSS 与 JavaScript 页签是否显示提交内容时是否保留 CSS/JS 段。在 modal.jsx 的handleUpdate中可以看到没有该权限的用户其 CSS 和 JS 段会在保存时被剥离只保留 HTML。同时若块内原本就含有 CSS/JS 而用户无权限弹窗顶部会显示一条不可关闭的警告 Notice这些内容将在保存时被移除。这一设计是为了避免内容经过服务端 kses 过滤后留下残缺标记——与其让用户看到被截断的半成品不如在编辑器内主动剥离并明示。六、CSS / JS 的分段存取parseContent与serializeContent编辑弹窗把 HTML、CSS、JS 分成三个独立编辑区这背后依赖 utils.js 中两个对称的工具函数parseContent( content )解析合并内容字符串。它创建一个临时 HTML 文档document.implementation.createHTMLDocument从中提取带data-wp-block-htmlcss标记的style标签和带data-wp-block-htmljs标记的script标签其余内容归为 HTML。注意未带标记的style/script标签不会被提取它们按普通 HTML 处理多个同类型标记标签只取第一个。serializeContent( { html, css, js } )反向操作按“CSS → JS → HTML”的顺序拼接并为 CSS/JS 段重新加上data-wp-block-html标记空白段会被忽略。两者配合实现无损的往返round-trip序列化这在 test/utils.jsdom.test.js 中有完整验证——包括空内容、纯 CSS、纯 JS、三段俱全、畸形 HTML 容错、多标记标签取首个、空白裁剪等十余个用例。这套分段机制的价值在于编辑器把 CSS/JS 与 HTML 分开管理权限检查与内容剥离只需针对标记段进行不会误伤用户写在 HTML 里的style/script标签。七、编辑时内容如何回流onUpdate的再解析机制当用户在弹窗点击“Update”edit.jsx 的onUpdate会执行一次完整的“再解析”流程用parse()把新内容包进!-- wp:html --分隔符后解析得到parsedBlock静态 HTML 部分成为块的innerContent片段!-- wp:* --分隔段则成为内部块通过serialize()逐块比较新旧内部块若内部块标记未变化例如只改了周边的静态 HTML则保留原有内部块及其 clientId 与选中状态避免编辑静态代码时无辜重置内部块所有更新放在registry.batch()中批量执行保证updateBlock与replaceInnerBlocks原子性生效。这套机制保证了“静态 HTML 与可编辑内部块共存”模型的健壮性用户在弹窗里编辑的是整体源码但提交后其中被识别为块的部分依然保持为真正可交互的嵌套块。八、块转换从core/code一键转为 Custom HTMLtransforms.js 定义了块的转换规则支持从core/code代码块转换为 Custom HTML。转换逻辑值得细读const text create( { html } ).text; const [ block ] parse( !-- wp:html --\n${ text }\n!-- /wp:html -- ); return block ?? createBlock( core/html, {}, [], [ text ] );流程是先把代码块的 HTML 内容转成纯文本wordpress/rich-text的create().text再包上wp:html分隔符重新解析。这样做的目的是如果代码块里恰好含有!-- wp:paragraph --之类的块分隔符文本它们会被还原为可编辑的真实内部块而不是变成惰性注释文本。若解析失败则退化为仅含单个静态文本片段的core/html块。test/index.jsdom.test.js 用两个用例验证了这一行为包含分隔符的代码块转换后内部多出一个段落块innerContent含null占位而纯静态 HTML 的代码块转换后内部块数量为 0。九、注册与初始化init.js到initBlock块的初始化入口在 init.jsimport { init } from ./; export default init();index.js中的init调用initBlock({ name, metadata, settings })来自 ../utils/init-block完成registerBlockType注册。Custom HTML 块的完整源码目录结构如下方便读者继续深入block.json元数据定义edit.jsx编辑组件modal.jsx编辑弹窗preview.jsx沙箱预览save.js序列化逻辑返回 null标记来自 innerContenttransforms.js块转换utils.jsHTML/CSS/JS 分段工具editor.scss编辑器样式test/index.jsdom.test.js 与 test/utils.jsdom.test.js行为与工具函数测试十、前端渲染与安全边界理解“原样输出”的双刃剑最后需要明确 Custom HTML 块的运行边界静态存储块标记保存在文章内容中由 WordPress 前端原样输出不存在服务端渲染回调权限防线是否允许在块内保存script/style以及 CSS/JS 页签是否可用完全取决于用户的unfiltered_html能力无权限用户保存时 CSS/JS 段会被主动剥离以避免 kses 过滤产生残缺标记编辑期沙箱预览通过SandBoxiframe 隔离运行避免编辑器环境与预览内容互相干扰嵌套块能力静态 HTML 中嵌入的!-- wp:* --分隔符会被解析为内部块实现“整体不可分割、局部仍可编辑”的混合模型。因此在实际使用中Custom HTML 块适合有经验的开发者放置第三方嵌入代码、统计脚本或定制标记对于普通内容作者应优先使用官方块并借助权限体系控制谁能写入脚本类内容。总结本文从 官方块文档 出发结合 block.json 与 edit.jsx、modal.jsx、save.js、transforms.js、utils.js 等源码文件完整还原了 Custom HTML 块的设计与实现content属性的role: local语义与迁移逻辑、克制的 supports 配置、基于 innerContent 的静态序列化模型、三栏式编辑弹窗与沙箱预览、unfiltered HTML 权限下的 CSS/JS 剥离机制以及从core/code的智能转换。理解这些内部机制无论对插件开发、块扩展还是内容架构设计都能提供扎实的底层依据。【免费下载链接】gutenbergThe Block Editor project for WordPress and beyond. Plugin is available from the official repository.项目地址: https://gitcode.com/GitHub_Trending/gu/gutenberg创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考