十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Gutenberg 区块过滤器(Block Filters)实战指南:从注册到渲染的 PHP 与 JavaScript 过滤钩子全解析

Gutenberg 区块过滤器(Block Filters)实战指南:从注册到渲染的 PHP 与 JavaScript 过滤钩子全解析 Gutenberg 区块过滤器Block Filters实战指南从注册到渲染的 PHP 与 JavaScript 过滤钩子全解析【免费下载链接】gutenbergThe Block Editor project for WordPress and beyond. Plugin is available from the official repository.项目地址: https://gitcode.com/GitHub_Trending/gu/gutenberg导读WordPress 区块编辑器Gutenberg开放了多组过滤器filters允许插件与主题开发者在不改动核心源码的前提下修改既有区块的注册信息、编辑器界面行为和前端渲染输出。本文以 Gutenberg 项目的官方参考文档为骨架逐一讲解服务端PHP与客户端JavaScript两类过滤钩子的参数签名、回调时机与完整代码示例并结合本仓库中的源码实现如packages/blocks/src/store/process-block-type.ts、packages/blocks/src/api/serializer.tsx、lib/block-supports/*.php等说明其底层生效原理。读完本文你将能独立实现批量修改区块元数据、禁用指定区块的颜色控制、为前端输出注入自定义类名、给编辑器侧栏添加自定义面板以及通过白名单/黑名单管理区块插入器。一、理解区块过滤器的整体脉络区块在 WordPress 中通常同时在服务端PHP与客户端JavaScript通过block.json元数据注册。Gutenberg 提供的过滤器覆盖了三个层面注册阶段Registration在区块类型正式登记前修改其元数据或注册参数前端渲染Front end修改区块保存到页面中的 HTML 输出编辑器行为Editor修改区块的edit组件、save输出、属性解析等编辑态行为。服务端过滤器通过 PHP 的add_filter()挂载客户端过滤器通过wp.hooks.addFilter()挂载其底层实现位于 packages/hooks。关于区块注册的完整流程可参考 区块注册指南。二、注册阶段的过滤器2.1block_type_metadata修改原始block.json元数据该过滤器在服务端从block.json加载原始元数据后、尚未进入正式处理流程前触发是最先介入的入口。回调只接收一个参数参数类型说明$metadataarray从block.json加载的区块类型元数据以下示例将所有区块的apiVersion强制设为2function example_filter_metadata_registration( $metadata ) { $metadata[apiVersion] 2; return $metadata; }; add_filter( block_type_metadata, example_filter_metadata_registration );更典型的实战场景是裁剪编辑器体验curating the Editor experience禁用 Heading 区块的背景色与渐变支持。因为此时元数据尚未处理你可以直接修改supports.color结构function example_disable_heading_background_color_and_gradients( $metadata ) { // Only apply the filter to Heading blocks. if ( ! isset( $metadata[name] ) || core/heading ! $metadata[name] ) { return $metadata; } // Check if supports key exists. if ( isset( $metadata[supports] ) isset( $metadata[supports][color] ) ) { // Remove Background color and Gradients support. $metadata[supports][color][background] false; $metadata[supports][color][gradients] false; } return $metadata; } add_filter( block_type_metadata, example_disable_heading_background_color_and_gradients );值得注意的是Gutenberg 仓库自身也大量依赖这个过滤器族来迁移旧有支持标志。例如 lib/block-supports/duotone.php 中通过block_type_metadata_settings挂载WP_Duotone_Gutenberg::migrate_experimental_duotone_support_flag将实验性的 duotone 支持标志迁移为标准结构lib/experimental/script-modules.php 则通过该过滤器为区块设置viewScriptModule等视图模块配置。这说明注册阶段过滤器是核心功能扩展的通用通道。2.2block_type_metadata_settings修改处理后的设置数组当元数据经过默认流程处理后会得到一份设置数组settings此时可以使用该过滤器施加默认处理未覆盖的自定义修改。回调接收两个参数参数类型说明$settingsarray为注册区块类型确定的设置数组$metadataarray从block.json加载的元数据以下示例将所有区块的apiVersion增加1function example_filter_metadata_registration( $settings, $metadata ) { $settings[api_version] $metadata[apiVersion] 1; return $settings; }; add_filter( block_type_metadata_settings, example_filter_metadata_registration, 10, 2 );注意这里操作的是处理后的$settings[api_version]下划线命名而元数据中则是apiVersion驼峰命名两者属于不同的数据形态。2.3register_block_type_args最底层的服务端注册参数钩子该过滤器在区块类型正式注册前的最后一步触发是服务端可用的最底层过滤器对所有在服务端注册的区块都生效。回调接收两个参数参数类型说明$argsarray用于注册区块类型的参数数组$block_typestring包含命名空间的区块类型名如core/paragraph服务端定义的所有设置都会以高于客户端设置的优先级传播到客户端因此用它做全局控制非常可靠。以下代码禁用 Paragraph、Heading、List、List Item 四个区块的颜色控件function example_disable_color_for_specific_blocks( $args, $block_type ) { // List of block types to modify. $block_types_to_modify [ core/paragraph, core/heading, core/list, core/list-item ]; // Check if the current block type is in the list. if ( in_array( $block_type, $block_types_to_modify, true ) ) { // Disable color controls. $args[supports][color] array( text false, background false, link false, ); } return $args; } add_filter( register_block_type_args, example_disable_color_for_specific_blocks, 10, 2 );2.4blocks.registerBlockType客户端注册设置过滤器这是客户端JavaScript侧注册区块时最核心的过滤器。它接收区块设置、区块名称以及第三个参数——null或已注册的废弃版本deprecation设置该过滤器同样会应用于每个区块的废弃deprecated设置。本仓库中process-block-type.ts正是它的实际调用点在 packages/blocks/src/store/process-block-type.ts 中注册流程对完整设置对象执行const settings applyFilters( blocks.registerBlockType, blockType, name, null );随后同一文件的 145-170 行 会对每个deprecated条目再次调用同一个过滤器第三个参数传入该条废弃设置本身——这正是文档所说过滤器也应用于每个区块的废弃设置的源码依据。下面的示例确保 List 区块使用规范生成类名wp-block-listfunction addListBlockClassName( settings, name ) { if ( name ! core/list ) { return settings; } return { ...settings, supports: { ...settings.supports, className: true, }, }; } wp.hooks.addFilter( blocks.registerBlockType, my-plugin/class-names/list-block, addListBlockClassName );三、前端渲染过滤器以下 PHP 过滤器用于改变区块在前端的输出它们不影响编辑器内的行为。3.1render_block拦截任意区块的前端输出该过滤器接收三个参数参数类型说明$block_contentstring区块内容$blockarray完整区块包含名称与属性$instanceWP_Block区块实例下面的示例为所有前端 Paragraph 区块添加example-class类名。推荐使用 WordPress HTML APIWP_HTML_Tag_Processor而非正则以保证健壮性function example_add_custom_class_to_paragraph_block( $block_content, $block ) { // Check if the block is a Paragraph block. if ( core/paragraph $block[blockName] ) { // Add the custom class to the block content using the HTML API. $processor new WP_HTML_Tag_Processor( $block_content ); if ( $processor-next_tag( p ) ) { $processor-add_class( example-class ); } return $processor-get_updated_html(); } return $block_content; } add_filter( render_block, example_add_custom_class_to_paragraph_block, 10, 2 );Gutenberg 仓库自身即大量使用render_block实现区块支持功能例如 lib/block-supports/typography.php 挂载gutenberg_render_typography_support、lib/block-supports/dimensions.php 挂载gutenberg_render_dimensions_support、lib/block-supports/duotone.php 挂载WP_Duotone_Gutenberg::render_duotone_support第三个参数传入$instance。可以推断在 Gutenberg 中支持supports体系的实现正是以render_block过滤器为核心管道在渲染时向 HTML 输出注入样式与类名。3.2render_block_{namespace/block}针对特定区块的简化形式当只需要修改某个具体区块时可以使用动态钩子名render_block_core/paragraph之类的形式免去在回调内判断区块类型的步骤。回调参数与render_block完全一致$block_content、$block、$instance。function example_add_custom_class_to_paragraph_block( $block_content, $block ) { // Add the custom class to the block content using the HTML API. $processor new WP_HTML_Tag_Processor( $block_content ); if ( $processor-next_tag( p ) ) { $processor-add_class( example-class ); } return $processor-get_updated_html(); } add_filter( render_block_core/paragraph, example_add_custom_class_to_paragraph_block, 10, 2 );这种按区块名命名的动态过滤器非常适合只改一种区块的场景例如在 lib/compat/wordpress-7.1/block-bindings.php 中通过render_block恢复列表项内部区块的做法也可改写为更精准的render_block_core/list-item形式。四、编辑器JavaScript过滤器4.1blocks.getSaveElement替换或包装save输出元素该过滤器作用于区块save函数的返回结果可通过React.cloneElement修改元素 props、替换子元素或返回一个全新的元素。回调接收三个参数element待修改的元素对象、blockType区块类型定义对象、attributes区块属性。下面的示例将 Cover 区块包装进外层divfunction wrapCoverBlockInContainer( element, blockType, attributes ) { // Skip if element is undefined. if ( ! element ) { return; } // Only apply to Cover blocks. if ( blockType.name ! core/cover ) { return element; } // Return the element wrapped in a div. return div classNamecover-block-wrapper{ element }/div; } wp.hooks.addFilter( blocks.getSaveElement, my-plugin/wrap-cover-block-in-container, wrapCoverBlockInContainer );4.2blocks.getSaveContent.extraProps向保存元素根节点添加额外属性该过滤器应用于所有在save函数中返回 WP Element 的区块用于向根元素添加className、id等合法 props。回调参数为props当前 save 元素的 props、blockType、attributes。以下示例为所有区块默认添加红色背景function addBackgroundColorStyle( props ) { return { ...props, style: { backgroundColor: red }, }; } wp.hooks.addFilter( blocks.getSaveContent.extraProps, my-plugin/add-background-color-style, addBackgroundColorStyle );重要警告如果该过滤器修改了已存在的内容下次编辑该文章时会触发区块校验错误——编辑器会校验文章中存储的内容与save()输出是否一致。相关机制详见 区块校验validation。要避免此问题修改既有文章内容请改用服务端render_block过滤器。在源码层面getSaveElement与blocks.getSaveContent.extraProps的实现位于 packages/blocks/src/api/serializer.tsx第 28-35 行的getBlockDefaultClassName先经blocks.getBlockDefaultClassName过滤得到默认类名随后在第 80、167-168 行分别应用blocks.getSaveContent.extraProps第 189-190 行通过blocks.getSaveElement应用 save 元素的过滤最终得到用于校验与保存的元素。4.3blocks.getBlockDefaultClassName自定义默认类名区块生成的 HTML 类名遵循wp-block-{name}命名规范该过滤器允许提供替代类名// Our filter function. function setBlockCustomClassName( className, blockName ) { return blockName core/code ? my-plugin-code : className; } // Adding the filter. wp.hooks.addFilter( blocks.getBlockDefaultClassName, my-plugin/set-block-custom-class-name, setBlockCustomClassName );4.4blocks.switchToBlockType.transformedBlock过滤单个转换结果用于过滤区块转换block transformation的单个结果。由于转换是多对多而非一对一原始区块会全部传入。4.5blocks.getBlockAttributes在解析与校验之间干预属性该过滤器在区块属性默认解析完成后、校验开始前立即调用允许插件在校验前以及编辑器初次渲染前调整属性值。回调接收 4 个参数参数类型说明blockAttributesObject全部区块属性blockTypeObject区块类型innerHTMLstring原始区块内容attributesobject已知区块属性来自定界符它在源码中的调用点位于 packages/blocks/src/api/parser/get-block-attributes.ts即解析流程的收尾处。下面的示例为所有 Paragraph 区块锁定移动lock: move// Our filter function function lockParagraphs( blockAttributes, blockType, innerHTML, attributes ) { if(core/paragraph blockType.name) { blockAttributes[lock] {move: true} } return blockAttributes; } // Add the filter wp.hooks.addFilter( blocks.getBlockAttributes, my-plugin/lock-paragraphs, lockParagraphs );4.6editor.BlockEdit包装区块的edit组件该过滤器接收原始BlockEdit组件并返回一个新的包装组件常用于向所有区块注入自定义 UI。以下示例为所有区块添加一个 Inspector 面板const { createHigherOrderComponent } wp.compose; const { InspectorControls } wp.blockEditor; const { PanelBody } wp.components; const withMyPluginControls createHigherOrderComponent( ( BlockEdit ) { return ( props ) { return ( BlockEdit keyedit { ...props } / InspectorControls PanelBodyMy custom control/PanelBody /InspectorControls / ); }; }, withMyPluginControls ); wp.hooks.addFilter( editor.BlockEdit, my-plugin/with-inspector-controls, withMyPluginControls );性能提醒该钩子对所有区块都会运行可能造成性能回退尤其是区块选择指标。应尽量让额外工作只在特定条件下执行。例如组件只需在区块被选中时渲染就应利用props.isSelected条件化渲染const withMyPluginControls createHigherOrderComponent( ( BlockEdit ) { return ( props ) { return ( BlockEdit { ...props } / { props.isSelected ( InspectorControls PanelBodyMy custom control/PanelBody /InspectorControls ) } / ); }; }, withMyPluginControls );4.7editor.BlockListBlock包装区块的列表包裹组件该过滤器用于修改包裹着edit组件与所有工具栏的区块包裹组件。以下示例为所有区块添加基于clientId的唯一类名const { createHigherOrderComponent } wp.compose; const withClientIdClassName createHigherOrderComponent( ( BlockListBlock ) { return ( props ) { return ( BlockListBlock { ...props } className{ block- props.clientId } / ); }; }, withClientIdClassName ); wp.hooks.addFilter( editor.BlockListBlock, my-plugin/with-client-id-class-name, withClientIdClassName );你还可以通过返回组件的wrapperProps属性向包裹组件添加新属性const { createHigherOrderComponent } wp.compose; const withMyWrapperProp createHigherOrderComponent( ( BlockListBlock ) { return ( props ) { const wrapperProps { ...props.wrapperProps, data-my-property: the-value, }; return BlockListBlock { ...props } wrapperProps{ wrapperProps } /; }; }, withMyWrapperProp ); wp.hooks.addFilter( editor.BlockListBlock, my-plugin/with-my-wrapper-prop, withMyWrapperProp );在仓库中editor.BlockListBlock的实际挂载点位于 packages/block-editor/src/components/block-list/block.jsx通过withFilters( editor.BlockListBlock )高阶组件接入区块列表渲染管线区块编辑组件本身则通过editor.BlockEdit接入。4.8editor.postContentBlockTypes锁定模板中的内容型区块该过滤器用于修改即使模板被锁定也应保持可用的区块列表。任何向文章保存数据的区块都应加入其中例如文章特色图片Post Featured Image区块即使模板锁定也应允许选择图片。以下示例启用虚拟区块namespace/exampleconst addExampleBlockToPostContentBlockTypes ( blockTypes ) { return [ ...blockTypes, namespace/example ]; }; wp.hooks.addFilter( editor.postContentBlockTypes, my-plugin/post-content-block-types, addExampleBlockToPostContentBlockTypes );五、移除区块黑名单与白名单5.1 黑名单使用unregisterBlockType注销区块插件或主题作者可以在 JavaScript 中通过黑名单注销区块。将以下代码放入my-plugin.js// my-plugin.js import { unregisterBlockType } from wordpress/blocks; import domReady from wordpress/dom-ready; domReady( function () { unregisterBlockType( core/verse ); } );然后在 PHP 中于编辑器加载该脚本?php // my-plugin.php function my_plugin_deny_list_blocks() { wp_enqueue_script( my-plugin-deny-list-blocks, plugins_url( my-plugin.js, __FILE__ ), array( wp-blocks, wp-dom-ready, wp-edit-post ) ); } add_action( enqueue_block_editor_assets, my_plugin_deny_list_blocks );⚠️ 竞态条件Race Condition警告注销区块与注册区块存在执行先后竞争你希望注销代码最后运行。因此必须把正在注册该区块的组件此处为wp-edit-post声明为脚本依赖同时使用wp.domReady()确保 DOM 加载完成后再执行注销。5.2 白名单仅保留指定区块若要禁用除白名单外的所有区块可改写上述脚本// my-plugin.js var allowedBlocks [ core/paragraph, core/image, core/html, core/freeform, ]; wp.blocks.getBlockTypes().forEach( function ( blockType ) { if ( allowedBlocks.indexOf( blockType.name ) -1 ) { wp.blocks.unregisterBlockType( blockType.name ); } } );六、从插入器中隐藏区块allowed_block_types_all服务端可用allowed_block_types_all过滤器控制插入器中显示的区块列表返回true全部支持、false全部不支持或区块名数组。第二个参数$editor_context允许根据内容上下文如文章类型做精细化过滤。⚠️ 版本说明WordPress 5.8 之前该钩子名为allowed_block_types现已废弃。若需兼容旧版本可检测WP_Block_Editor_Context类是否存在该类在 5.8 引入来判断应使用哪个过滤器。?php // my-plugin.php function example_filter_allowed_block_types_when_post_provided( $allowed_block_types, $editor_context ) { if ( ! empty( $editor_context-post ) ) { return array( core/paragraph, core/heading ); } return $allowed_block_types; } add_filter( allowed_block_types_all, example_filter_allowed_block_types_when_post_provided, 10, 2 );七、管理区块分类7.1block_categories_all过滤默认区块分类同样在服务端实现该过滤器接收分类列表并在区块注册、插入器分组时使用第二参数$editor_context可用于按内容过滤。⚠️ 版本说明WordPress 5.8 之前该钩子名为block_categories现已废弃兼容性检测方式与allowed_block_types_all相同检测WP_Block_Editor_Context是否存在。// my-plugin.php function example_filter_block_categories_when_post_provided( $block_categories, $editor_context ) { if ( ! empty( $editor_context-post ) ) { array_push( $block_categories, array( slug custom-category, title __( Custom Category, custom-plugin ), icon null, ) ); } return $block_categories; } add_filter( block_categories_all, example_filter_block_categories_when_post_provided, 10, 2 );7.2wp.blocks.updateCategory为分类设置自定义图标分类可通过icon属性显示图标值可以是 WordPress Dashicon 的 slug也可以是在前端渲染好的 SVG以兼容移动端并提升可访问性。为上一示例中的分类设置 SVG 图标( function () { var el React.createElement; var SVG wp.primitives.SVG; var circle el( circle, { cx: 10, cy: 10, r: 10, fill: red, stroke: blue, strokeWidth: 10, } ); var svgIcon el( SVG, { width: 20, height: 20, viewBox: 0 0 20 20 }, circle ); wp.blocks.updateCategory( my-category, { icon: svgIcon } ); } )();八、过滤器选用速查场景过滤器语言时机修改block.json原始元数据block_type_metadataPHP服务端注册前修改处理后的区块设置block_type_metadata_settingsPHP服务端注册前修改最终注册参数全局最低层register_block_type_argsPHP服务端注册时修改客户端注册设置含废弃版本blocks.registerBlockTypeJS客户端注册时修改任意区块前端输出render_blockPHP前端渲染修改特定区块前端输出render_block_{name}PHP前端渲染包装/替换save元素blocks.getSaveElementJS保存序列化为保存根元素添加 propsblocks.getSaveContent.extraPropsJS保存序列化自定义默认类名blocks.getBlockDefaultClassNameJS保存序列化校验前干预属性解析blocks.getBlockAttributesJS属性解析后包装edit组件editor.BlockEditJS编辑器渲染包装区块列表包裹组件editor.BlockListBlockJS编辑器渲染锁定模板中仍启用的区块editor.postContentBlockTypesJS模板锁定控制插入器允许的区块allowed_block_types_allPHP编辑器初始化管理区块分类列表block_categories_allPHP注册/插入器更新分类图标wp.blocks.updateCategoryJS编辑器初始化结语与深入阅读本文从注册、渲染、编辑器三个层面梳理了 Gutenberg 区块过滤器的完整体系。实践时建议遵循两条原则修改既有文章内容优先使用服务端render_block避免blocks.getSaveContent.extraProps引发的校验错误对editor.BlockEdit这类全量钩子务必做好条件化渲染防止编辑器性能回退。相关延伸资料可在本仓库继续阅读区块注册指南、save与校验机制、裁剪编辑器体验指南以及底层过滤器实现 process-block-type.ts 与 serializer.tsx。【免费下载链接】gutenbergThe Block Editor project for WordPress and beyond. Plugin is available from the official repository.项目地址: https://gitcode.com/GitHub_Trending/gu/gutenberg创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表