Gutenberg Formatting Toolbar API 开发指南:为富文本工具栏添加自定义格式按钮
【免费下载链接】gutenbergThe Block Editor project for WordPress and beyond. Plugin is available from the official repository.项目地址: https://gitcode.com/GitHub_Trending/gu/gutenberg
Formatting Toolbar API(格式工具栏 API)是 Gutenberg 块编辑器为开发者提供的官方扩展机制,用于向富文本格式化工具栏添加自定义按钮,并让按钮对选中的文本应用一种"格式"(format)。本指南以 Gutenberg 仓库 docs/how-to-guides/format-api.md 为骨架,结合@wordpress/rich-text、@wordpress/block-editor等包的源码实现,逐步讲解如何注册格式类型、渲染工具栏按钮、切换格式作用域,以及按块类型条件显示按钮,最终在插件中构建出与内置 Bold、Italic 同等体验的自定义格式能力。
什么是 Format API
Format API 让开发者可以为格式化工具栏添加自定义按钮,并使按钮将一种format应用到文本选区上。在 WordPress 的术语体系中,format是一个具有文本级语义的 HTML 标签,用来给一段选中的文本赋予特殊含义。例如本教程中挂载到格式工具栏的按钮,会把选中的文本用<samp>标签包裹起来。
内置的粗体(Bold)按钮就是格式化工具栏中标准按钮的典型代表。在 Gutenberg 源码中,粗体正是通过 Format API 实现的:packages/format-library/src/bold/index.tsx定义了core/bold格式,其tagName为strong、className为null,并在edit渲染函数中通过RichTextToolbarButton组件输出按钮、通过RichTextShortcut绑定快捷键,最后调用toggleFormat切换格式:
// packages/format-library/src/bold/index.tsx(节选) export const bold = { name, title, tagName: 'strong', className: null, edit( { isActive, value, onChange, onFocus, isVisible = true } ) { function onToggle() { onChange( toggleFormat( value, { type: name, title } ) ); } // ... return ( <> <RichTextShortcut type="primary" character="b" onUse={ onToggle } /> <RichTextToolbarButton name="bold" icon={ formatBold } title={ title } onClick={ onClick } isActive={ isActive } shortcutType="primary" shortcutCharacter="b" /> </> ); }, };也就是说,你的自定义按钮与编辑器内置的粗体、斜体等按钮走的是同一条注册管线,这正是本指南想让你掌握的核心能力。
开始之前
本指南假定你已经熟悉 WordPress 插件的基本概念,以及如何在插件中加载 JavaScript。可以参阅 插件手册 复习相关知识。
你需要准备:
- WordPress 开发环境
- 一个已激活、配置就绪的最小插件
- 用于构建和入队 JavaScript 的工具链
本指南以src/index.js作为修改的 JavaScript 文件。完成每一步后,运行npm run build会生成build/index.js,该文件随后被加载到文章编辑器页面中。
提示:构建产物(
build/index.js)需要通过插件的 PHP 文件使用wp_enqueue_script正确入队,并在注册脚本时声明@wordpress/rich-text、@wordpress/block-editor等依赖(或依赖wp-rich-text、wp-block-editor等 WordPress 全局脚本句柄),编辑器才能识别这些模块的导入。
Step 1:注册一个新的格式类型
第一步是注册新格式。创建src/index.js并写入以下内容:
import { registerFormatType } from '@wordpress/rich-text'; registerFormatType( 'my-custom-format/sample-output', { title: 'Sample output', tagName: 'samp', className: null, } );注册背后的校验逻辑
registerFormatType由@wordpress/rich-text包导出(见 packages/rich-text/src/index.ts),其实现位于 packages/rich-text/src/register-format-type.js。该函数在真正写入 store 之前会做一系列校验,理解这些规则可以帮你避开常见的注册失败陷阱:
- 名称必须是字符串,否则报错
Format names must be strings.; - 名称必须包含命名空间前缀,格式为
[a-z][a-z0-9-]*\/[a-z][a-z0-9-]*——即两个小写字母/数字/连字符组成的段,中间用斜杠分隔,且必须以字母开头。例如my-custom-format/sample-output合法,而doing-it-wrong(缺命名空间)、doing/it/wrong(两个斜杠)、Core/Bold(大写)都会被拒绝; - 名称不能与已注册格式重复;
tagName必须是非空字符串;className必须是字符串或null。为null时表示"裸元素"格式(不带 class 的标签),此时该tagName不能已有其他格式注册;为字符串时必须匹配^[_a-zA-Z]+[a-zA-Z0-9_-]*$(以字母开头,后可跟字母、数字、连字符、下划线),且该 class 不能已被其他格式占用;- 必须提供
title,且为字符串;keywords可选但最多 3 个。
这些校验规则均有对应的单元测试覆盖,见 packages/rich-text/src/test/register-format-type.jsdom.test.js,例如"无命名空间""多斜杠""大写名称""重复 tagName/className"等场景都会断言报错并返回undefined。
用数据 store 验证注册结果
已注册的格式类型列表维护在core/rich-text数据 store 中。你可以在浏览器控制台执行以下代码确认自定义格式已生效:
wp.data.select( 'core/rich-text' ).getFormatTypes();该调用会返回一个包含所有格式类型的数组,其中就包括你刚注册的my-custom-format/sample-output。
从源码看,store 的选择器定义在 packages/rich-text/src/store/selectors.js:getFormatTypes基于state.formatTypes返回全部格式;getFormatType( name )按名称精确查找;还有getFormatTypeForBareElement与getFormatTypeForClassName两个辅助选择器,分别用于按裸标签名或 class 名反查格式——这也是注册校验阶段判断"冲突"所依赖的内部机制。写入动作则定义在 packages/rich-text/src/store/actions.js,ADD_FORMAT_TYPES/REMOVE_FORMAT_TYPES对应格式的增删。
Step 2:向工具栏添加按钮
格式注册好之后,下一步是注册一个edit组件,把按钮渲染到界面上。edit属性(见 register-format-type.js 中的WPFormat类型定义)应返回一个组件,让用户与新注册的格式进行交互。
使用RichTextToolbarButton组件更新src/index.js:
import { registerFormatType } from '@wordpress/rich-text'; import { RichTextToolbarButton } from '@wordpress/block-editor'; const MyCustomButton = ( props ) => { return ( <RichTextToolbarButton icon="editor-code" title="Sample output" onClick={ () => { console.log( 'toggle format' ); } } /> ); }; registerFormatType( 'my-custom-format/sample-output', { title: 'Sample output', tagName: 'samp', className: null, edit: MyCustomButton, } );构建并重新加载编辑器后,选中任意包含文本的块(例如段落块),确认新按钮已出现在格式工具栏中。点击按钮,检查控制台是否输出toggle format消息。
如果看不到按钮或消息,请检查:JavaScript 是否正确构建并加载、构建过程有无语法错误、以及控制台是否有报错信息。
edit组件接收的 props
从 bold/index.tsx 的实际实现可以看出,edit组件会收到isActive、value、onChange、onFocus等 props(value为当前的RichTextValue,onChange用于提交新的富文本值,isActive表示当前选区是否处于该格式下)。RichTextToolbarButton组件由@wordpress/block-editor包提供,参考文档见 packages/block-editor/README.md。
Step 3:点击按钮应用格式
下一步是让按钮真正对选中文本应用格式。对于本例的<samp>标签来说,格式是二元的——要么选中文本带有该标签,要么没有——因此可以直接使用@wordpress/rich-text包中的toggleFormat方法。
更新src/index.js,改写onClick动作:
import { registerFormatType, toggleFormat } from '@wordpress/rich-text'; import { RichTextToolbarButton } from '@wordpress/block-editor'; const MyCustomButton = ( { isActive, onChange, value } ) => { return ( <RichTextToolbarButton icon="editor-code" title="Sample output" onClick={ () => { onChange( toggleFormat( value, { type: 'my-custom-format/sample-output', } ) ); } } isActive={ isActive } /> ); }; registerFormatType( 'my-custom-format/sample-output', { title: 'Sample output', tagName: 'samp', className: null, edit: MyCustomButton, } );验证步骤:先构建并重新加载,然后选中一段文本并点击按钮,浏览器中该选区会以与周围文本不同的样式显示(<samp>默认有等宽字体等样式)。你还可以切换到 HTML 视图(代码编辑器Ctrl+Shift+Alt+M)确认选中文本已被<samp>标签包裹。
toggleFormat 的底层工作原理
toggleFormat的实现位于 packages/rich-text/src/toggle-format.js。它的逻辑非常直观:
- 调用
getActiveFormat( value, format.type )检查当前选区起始处是否存在同类型格式; - 若存在则调用
removeFormat移除该格式,并向屏幕阅读器播报 "{title}removed."; - 若不存在则调用
applyFormat应用该格式,并播报 "{title}applied."。
getActiveFormat(见 get-active-format.js)内部通过getActiveFormats取回选区起始处的格式列表,再按type匹配查找。applyFormat(见 apply-format.js)则处理了选区折叠(光标无选中)与选区展开两种情形:折叠时若光标正好落在同类型格式中,会把范围扩展到该格式的边界以便更新属性;展开时则会把旧格式从区间内过滤掉,再把新格式插入到合适的位置,最后通过normaliseFormats规范化格式序列。removeFormat(见 remove-format.js)是对称的移除逻辑。
内置格式的
toggleFormat调用模式与你的自定义按钮完全一致——bold中就是onChange( toggleFormat( value, { type: name, title } ) )。这意味着自定义格式天生具备与粗体相同的"重复点击切换开/关"行为。
使用 className 定制样式
在注册时使用className选项可以为标签添加自定义 class。例如:
registerFormatType( 'my-custom-format/sample-output', { title: 'Sample output', tagName: 'samp', className: 'my-sample-output', edit: MyCustomButton, } );然后你可以用该 class 配合自定义 CSS 精确命中该元素并按需美化。注意:注册时className若为字符串,格式匹配将基于该 class;若为null,则代表"裸元素"格式(只按标签名匹配),二者在 store 中是分开维护的(对应getFormatTypeForBareElement与getFormatTypeForClassName两个选择器)。
Step 4:只在特定块类型中显示按钮(可选)
默认情况下,按钮会渲染在每个富文本工具栏上(图片说明、按钮、段落等)。如果希望按钮只出现在特定类型的块上,可以使用 data API(参考 packages/data)结合useSelect读取当前选中的块。
以下示例让按钮仅在段落块(Paragraph)中显示:
import { registerFormatType, toggleFormat } from '@wordpress/rich-text'; import { RichTextToolbarButton } from '@wordpress/block-editor'; import { useSelect } from '@wordpress/data'; function ConditionalButton( { isActive, onChange, value } ) { const selectedBlock = useSelect( ( select ) => { return select( 'core/block-editor' ).getSelectedBlock(); }, [] ); if ( selectedBlock && selectedBlock.name !== 'core/paragraph' ) { return null; } return ( <RichTextToolbarButton icon="editor-code" title="Sample output" onClick={ () => { onChange( toggleFormat( value, { type: 'my-custom-format/sample-output', } ) ); } } isActive={ isActive } /> ); } registerFormatType( 'my-custom-format/sample-output', { title: 'Sample output', tagName: 'samp', className: null, edit: ConditionalButton, } );这里通过select( 'core/block-editor' ).getSelectedBlock()获取当前选中的块对象,判断其name是否等于core/paragraph;不是则返回null不渲染按钮。你也可以据此扩展出"仅对按钮块、引文块生效"等更细粒度的可见性规则。
Step 5:把按钮放到下拉菜单之外(可选)
使用RichTextToolbarButton时,按钮会被放入默认的下拉菜单中。若想直接把按钮固定显示在工具栏上(不折叠进下拉菜单),可以改用BlockControls组件:
import { registerFormatType, toggleFormat } from '@wordpress/rich-text'; import { BlockControls } from '@wordpress/block-editor'; import { ToolbarGroup, ToolbarButton } from '@wordpress/components'; const MyCustomButton = ( { isActive, onChange, value } ) => { return ( <BlockControls> <ToolbarGroup> <ToolbarButton icon="editor-code" title="Sample output" onClick={ () => { onChange( toggleFormat( value, { type: 'my-custom-format/sample-output', } ) ); } } isActive={ isActive } /> </ToolbarGroup> </BlockControls> ); }; registerFormatType( 'my-custom-format/sample-output', { title: 'Sample output', tagName: 'samp', className: null, edit: MyCustomButton, } );此时按钮通过BlockControls被渲染到块级工具栏(block toolbar)上,而不是富文本浮层工具栏的下拉菜单中。
Troubleshooting:常见问题排查
如果遇到错误,按以下顺序排查:
- 确认先运行了
npm run build; - 确认构建过程没有语法错误或其他问题;
- 确认 JavaScript 确实被加载到了编辑器中(例如在浏览器 Network 面板检查脚本请求,或在控制台打印
window.wp相关全局对象); - 检查控制台是否有报错信息——尤其注意
registerFormatType的名称格式、tagName、className 校验报错(详见上文 Step 1 的校验规则); - 若按钮未显示,检查
edit组件是否在条件渲染中意外返回了null,以及当前块类型是否符合可见性条件。
更多可用的 RichText API
本指南主要使用了registerFormatType与toggleFormat,@wordpress/rich-text还提供了其他常用 API,完整文档见 packages/rich-text/README.md:
applyFormat:把格式对象应用到当前选区的富文本值上,返回新值;removeFormat:按格式类型从选区移除格式;toggleFormat:在应用与移除之间切换(本指南 Step 3 的核心);registerFormatType:注册新格式(Step 1);unregisterFormatType:反注册格式,实现位于 packages/rich-text/src/unregister-format-type.js,若格式未注册会报错并返回undefined。
此外,若你的格式需要根据选区定位弹出 UI(如链接格式的输入框),可以借助useAnchorhook(见 packages/rich-text/README.md),它返回当前被格式化的元素,或在无格式活动时为选区范围构造虚拟元素,适合传给Popover组件的anchor属性。
结论
本指南演示了如何向工具栏添加按钮,并让按钮对选中文本应用格式:从registerFormatType注册格式类型、用RichTextToolbarButton渲染按钮、用toggleFormat实现切换,再到按块类型条件渲染与使用BlockControls将按钮固定到工具栏。内置的core/bold、core/italic、core/link等格式(全部定义于 packages/format-library/src/default-formats.ts,并在 packages/format-library/src/index.ts 中批量注册)都是这套机制的直接产物。动手在下一个插件中试试,探索 Format API 能为你带来怎样的编辑体验增强吧。
【免费下载链接】gutenbergThe Block Editor project for WordPress and beyond. Plugin is available from the official repository.项目地址: https://gitcode.com/GitHub_Trending/gu/gutenberg
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考