ARTICLE DETAIL

资讯详情

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

Gutenberg RichText 组件完全指南:从富文本渲染到格式工具栏扩展

Gutenberg RichText 组件完全指南:从富文本渲染到格式工具栏扩展 Gutenberg RichText 组件完全指南从富文本渲染到格式工具栏扩展【免费下载链接】gutenbergThe Block Editor project for WordPress and beyond. Plugin is available from the official repository.项目地址: https://gitcode.com/GitHub_Trending/gu/gutenberg导读RichText是 Gutenberg 块编辑器wordpress/block-editor中渲染可编辑富文本内容的核心组件它基于浏览器原生的contenteditable能力为 WordPress 块开发提供「所见即所得」的文本编辑体验并自动衔接块属性attribute、选区selection与格式工具栏format toolbar。本文将以packages/block-editor/src/components/rich-text/README.md为骨架结合当前仓库源码完整讲解RichText的全部公开属性、RichText.Content的保存机制、RichTextToolbarButton的格式扩展方案以及它们背后的实现原理帮助读者在自定义块中正确、完整地接入富文本字段并扩展自己的格式按钮。RichText 是什么根据 RichText 官方文档RichText渲染一个富文本contenteditable输入元素为用户提供格式化内容的能力。它是「可编辑 HTML 字符串」与「块属性存储」之间的桥梁编辑态在块的edit函数中渲染为可编辑元素用户可直接输入、应用加粗/斜体/链接等格式保存态通过RichText.Content在save函数中输出最终 HTML保证前端渲染与编辑内容一致。从源码看RichText的公开导出位于 packages/block-editor/src/components/rich-text/index.jsx组件默认导出经过forwardRef包装的PublicForwardedRichTextContainer并在其上挂载了Content与isEmpty静态方法见该文件末尾PublicForwardedRichTextContainer.Content Content; PublicForwardedRichTextContainer.isEmpty ( value ) { return ! value || value.length 0; };同时RichText、RichTextShortcut、RichTextToolbarButton、__unstableRichTextInputEvent均从 packages/block-editor/src/components/index.js 对外统一导出即开发者通常使用的import { RichText, RichTextToolbarButton } from wordpress/block-editor。核心属性详解RichText的所有属性都是可复用的 React props。下文逐一说明其语义、默认值、使用场景并补充源码层面的实现证据。value: String必填需要被编辑的HTML 字符串。该 HTML 必须合法且当提供了tagName时内容必须能合法地嵌套在该标签内部。它是受控属性配合onChange形成「值 → 输入 → 值」的闭环。从 hook/index.js 的实现看useRichText内部会通过RichTextData.fromHTMLString( value, { preserveWhiteSpace } )将 HTML 字符串解析为可编辑的富文本记录RichTextData再经由to-dom的apply应用到 DOM 上每次属性变化都会触发记录重建与 DOM 同步_valueRef.current value; recordRef.current value; if ( ! ( value instanceof RichTextData ) ) { recordRef.current value ? RichTextData.fromHTMLString( value, { preserveWhiteSpace } ) : RichTextData.empty(); }注意早期版本允许value为 children 数组类型该用法已在content.jsx的valueToHTMLString中声明弃用自 6.1 起6.3 移除应始终传入字符串。onChange( value: String ): Function必填值变化时被调用参数为更新后的 HTML 字符串。典型用法是写回块属性onChange{ ( content ) setAttributes( { content } ) }identifier: String可选当可编辑字段绑定到块属性通过value/onChange时应传入属性名。编辑器使用它在块编辑器 store 中精确记录选区选区落在哪个属性、偏移量是多少选区起点/终点。源码中RichTextWrapper正是借助identifier订阅blockEditorStore的选区index.jsxisSelected selectionStart.clientId clientId selectionEnd.clientId clientId ( identifier ? selectionStart.attributeKey identifier : selectionStart[ instanceIdKey ] instanceId );可见传入identifier时选区按attributeKey匹配不传时则退化为按实例 ID 匹配。因此凡是绑定到块属性的字段都强烈建议传入identifier这能让选区在撤销、跨块操作时更稳定。组件最终还会把identifier写到 DOM 的data-wp-block-attribute-key属性上见index.jsx末尾的 JSX。tagName: String默认div可编辑元素的标签名例如h2、p、blockquote等。默认值为div源码RichTextWrapper解构默认值tagName div与此一致。传入的valueHTML 必须能合法地作为该标签的子内容。placeholder: String可选字段为空时显示的占位文本语义与原生input/textarea的placeholder属性一致。源码中占位符还承担了无障碍职责当未显式提供aria-label时RichTextWrapper会把placeholder用作aria-label见 index.jsx 中aria-label{ bindingsLabel || props[ aria-label ] || placeholder }。disableLineBreaks: Boolean可选设为true时禁用按Enter插入换行。从渲染结果看aria-multiline{ ! disableLineBreaks }即关闭换行时元素也被标注为非多行文本框实际拦截发生在事件监听层packages/block-editor/src/components/rich-text/event-listeners/enter.js当该值为true时 Enter 不再插入br。onReplace( blocks: Array ): Function可选当RichText实例可以被替换为给定 blocks数组时被调用。典型场景用户输入/触发块变换、粘贴 URL 生成新块等。例如段落块将其与块编辑器的onReplace透传见 paragraph/edit.jsxRichText onMerge{ mergeBlocks } onReplace{ onReplace } onRemove{ onRemove } ... /onMerge( forward: Boolean ): Function可选当相邻块可以合并时被调用。forward为true表示与后一个块合并为false表示与前一个块合并典型触发在段落开头按 Backspace / 结尾按 Delete。onRemove( forward: Boolean ): Function可选当块可以被删除时被调用。forward为true表示选区预期移到后一个块为false移到前一个块。段落块的实现直接复用了onReplace的删除语义onRemove{ onReplace ? () onReplace( [] ) : undefined }allowedFormats: Array可选默认情况下所有已注册的格式都允许使用。通过该属性可精确限制可用格式例如只允许加粗与斜体allowedFormats{ [ core/bold, core/italic ] }这些字符串即格式类型的名称registerFormatType注册的名字如core/bold、core/link。源码层面utils.jsx的getAllowedFormats还会结合内部属性__unstableDisableFormats当完全禁用格式时直接返回空数组export function getAllowedFormats( { allowedFormats, disableFormats } ) { if ( disableFormats ) { return getAllowedFormats.EMPTY_ARRAY; } return allowedFormats; }withoutInteractiveFormatting: Boolean可选默认情况下所有格式控件都存在。该属性用于移除会让内容变为「可交互内容」的格式控件如链接适用于「把已经是交互性的内容例如按钮内的文字变得可编辑」的场景避免编辑后的内容与原有交互行为冲突。从 HTML 规范看a等元素属于 interactive content。isSelected: Boolean可选控制是否显示输入框的选中态从而决定是否展示格式控件。默认行为是块被选中时渲染格式控件。RichTextWrapper中该参数被传入useRichText的__unstableIsSelected并驱动FormatToolbarContainer与FormatEdit的渲染{ isSelected ( KeyboardShortcutContext.Provider value{ keyboardShortcuts } ... FormatEdit ... / /KeyboardShortcutContext.Provider ) } { isSelected hasFormats ( FormatToolbarContainer inline{ inlineToolbar } editableContentElement{ anchorElement } / ) }autocompleters: ArrayCompleter可选一组用于替代默认自动补全的 completer 列表。源码中通过useBlockEditorAutocompleteProps生成自动补全所需的 DOM 属性aria-autocomplete、aria-haspopup、aria-controls、aria-activedescendant等并合并到可编辑元素上同时把补全状态的 ARIA 属性镜像到编辑宿主元素保证辅助技术可正确解析见 index.jsx。preserveWhiteSpace: Boolean可选是否保留value中的空白字符。默认情况下制表符、换行、连续空格会被折叠为单个空格或被 trim 掉设为true后这些字符将被保留。该值最终传给RichTextData.fromHTMLString( value, { preserveWhiteSpace } )从解析源头影响文本节点内容见 hook/index.js。完整示例注册一个带富文本标题的块README 给出了一个完整可运行的块注册示例将上述属性串联起来attributes中使用source: html从h2提取内容edit中渲染RichTextsave中渲染RichText.Contentimport { registerBlockType } from wordpress/blocks; import { RichText } from wordpress/block-editor; registerBlockType( /* ... */, { // ... attributes: { content: { source: html, selector: h2, }, }, edit( { className, attributes, setAttributes } ) { return ( RichText tagNameh2 className{ className } identifiercontent value{ attributes.content } onChange{ ( content ) setAttributes( { content } ) } / ); }, save( { attributes } ) { return RichText.Content tagNameh2 value{ attributes.content } /; } } );若想进一步限制格式并显示占位符可叠加 README 的 ESNext 风格示例RichText tagNameh2 identifiercontent value{ attributes.content } allowedFormats{ [ core/bold, core/italic ] } // 只允许加粗与斜体不允许其他格式 onChange{ ( content ) setAttributes( { content } ) } placeholder{ __( Heading... ) } /实战对照Heading 与 Paragraph 块的官方写法仓库内的核心块是RichText的最佳实战参考Heading 块packages/block-library/src/heading/edit.jsx同时使用了identifier、tagName、onMerge、onReplace、onRemove、placeholder与块属性透传RichText identifiercontent tagName{ tagName } value{ content } onChange{ onContentChange } onMerge{ mergeBlocks } onReplace{ onReplace } onRemove{ onReplace ? () onReplace( [] ) : undefined } placeholder{ placeholder || __( Heading ) } { ...blockProps } /其save函数packages/block-library/src/heading/save.jsx正是RichText.Content的标准用法return ( TagName { ...useBlockProps.save() } RichText.Content value{ content } / /TagName );Paragraph 块packages/block-library/src/paragraph/edit.jsx则展示了更进阶的玩法用RichText.isEmpty判断空内容、透传__unstableEmbedURLOnPaste粘贴 URL 自动转链接与__unstableAllowPrefixTransformations输入/等前缀触发块变换。RichText.Content在 save 中正确输出富文本RichText.Content应被用在块的save函数中以正确保存富文本内容。它的作用是把编辑期的富文本记录/字符串转换为最终输出的 HTML且不附加任何编辑态交互。其实现位于 content.jsxexport function Content( { value, tagName: Tag, multiline, format, ...props } ) { value RawHTML{ valueToHTMLString( value, multiline ) }/RawHTML; return Tag ? Tag { ...props }{ value }/Tag : value; }要点通过RawHTML输出 HTML保证保存内容不会被转义支持tagName包装如h2、pvalueToHTMLString对空值、字符串、RichTextData分别处理空值返回多行模式返回空标签对字符串原样返回RichTextData调用toHTMLString()序列化。兼容性提示value传 children 数组旧用法已在 content.jsx 中声明弃用6.1 起、6.3 移除请统一使用字符串。已弃用特性迁移提示当前仓库源码中RichText有两个值得注意的弃用点新代码应避免使用multiline属性自 6.1 弃用、6.3 移除。旧版用它支持多段落富文本内部按p或li拆分/合并值见 multiline.jsx 中的getMultilineTag与 deprecated 调用。官方建议迁移方案是改用嵌套块InnerBlocks来实现多段落/列表结构。onSplit属性自 6.4 弃用。RichTextWrapper构造时会触发deprecated( wp.blockEditor.RichText onSplit prop, { alternative: block.json support key: splitting } )即改用块的block.json中的supports.splitting声明块是否支持在编辑内容时拆分。通过 RichTextToolbarButton 扩展格式工具栏作用与用法RichTextToolbarButton是扩展格式工具栏的插槽Slot。在registerFormatType调用的edit函数中使用它即可把自定义格式按钮暴露到 UI 中。README 示例import { registerFormatType } from wordpress/rich-text; import { RichTextToolbarButton } from wordpress/block-editor; registerFormatType( /* ... */, { /* ... */ edit( { isActive } ) { return ( RichTextToolbarButton icon{ editor-code } title{ My formatting button } onClick{ /* ... */ } isActive{ isActive } / ); }, /* ... */ } );其实现位于 toolbar-button.jsx它本质上是向RichText.ToolbarControls可附加name后缀这一 Fill 注入一个ToolbarButtonexport function RichTextToolbarButton( { name, shortcutType, shortcutCharacter, ...props } ) { let fillName RichText.ToolbarControls; if ( name ) { fillName .${ name }; } if ( shortcutType shortcutCharacter ) { shortcut displayShortcut shortcutType ; } return ( Fill name{ fillName } ToolbarButton { ...props } shortcut{ shortcut } / /Fill ); }name决定按钮落在哪个格式槽位如bold、italic、link、unknownshortcutTypeshortcutCharacter通过wordpress/keycodes的displayShortcut渲染键盘快捷键提示如primaryk显示 ⌘K / CtrlK。工具栏如何收集这些按钮FormatToolbarpackages/block-editor/src/components/rich-text/format-toolbar/index.jsx负责渲染这些 Slot{ [ bold, italic, link, unknown ].map( ( format ) ( Slot name{ RichText.ToolbarControls.${ format } } key{ format } / ) ) } Slot nameRichText.ToolbarControls { ( fills ) { /* 收集多余按钮到 More 下拉菜单 */ } } /Slot即四个固定格式槽位bold/italic/link/unknown直接展示其余所有填充fills统一收进一个带chevronDown图标的「More」下拉菜单DropdownMenu并按title排序。若其中有任一按钮处于激活态isActive「More」按钮会呈现按压态样式。内联工具栏 vs 区块工具栏FormatToolbarContainerformat-toolbar-container.jsx决定工具栏的呈现位置传入inlineToolbar时通过Popoverplacementtop锚定当前可编辑元素渲染为浮动内联工具栏未传时渲染进BlockControls的groupinline即常规的块级工具栏。官方格式库中的真实案例wordpress/format-library的链接格式是RichTextToolbarButton的标准示范packages/format-library/src/link/index.tsx它同时注册快捷键、图标、标题、激活态与 ARIA 属性RichTextToolbarButton namelink icon{ linkIcon } title{ isActive ? __( Link ) : title } onClick{ ( event ) { addLink( event.currentTarget ); } } isActive{ isActive || addingLink } shortcutTypeprimary shortcutCharacterk aria-haspopuptrue aria-expanded{ addingLink } /其余如bold、italic、code、strikethrough、superscript、subscript、text-color、image、math等格式均在packages/format-library/src/下使用同样的模式注册是学习自定义格式的完整范例。深入源码RichText 的底层工作机制事件监听体系RichTextWrapper通过useEventListenerspackages/block-editor/src/components/rich-text/event-listeners/index.js把大量编辑行为挂到可编辑元素上event-listeners/目录下每个文件负责一类行为enter.jsEnter 键行为换行、disableLineBreaks拦截、块拆分delete.jsDelete/Backspace 删除行为触发onMerge/onRemovepaste-handler.js粘贴处理纯文本粘贴__unstablePastePlainText、URL 嵌入/转链接__unstableEmbedURLOnPasteinput-rules.js与before-input-rules.js输入规则如输入---转分隔线等魔法操作shortcuts.js快捷键如primaryb加粗remove-browser-shortcuts.js屏蔽浏览器默认快捷键干扰insert-replacement-text.js、input-events.js、firefox-compat.js替换文本插入、输入事件、Firefox 兼容处理。这些监听器共同构成了「contenteditable 上的完整编辑体验」正是RichText优于裸contentEditable的原因。富文本核心引擎RichText的可编辑核心并非自研 DOM 操作而是复用wordpress/rich-text包的能力useRichTextpackages/rich-text/src/hook/index.js负责「props 值 →RichTextData记录 → DOM 应用」的同步并维护选区selectionStart/selectionEnd格式类型的注册/注销由registerFormatType/unregisterFormatType提供packages/rich-text/src/register-format-type.js、packages/rich-text/src/unregister-format-type.jsRichTextShortcut与RichTextInputEvent现也由wordpress/rich-text提供block-editor仅为向后兼容而重新导出见 index.jsx 中的注释说明。与块选区的联动RichTextWrapper仅当块被选中时才订阅blockEditorStore的getSelectionStart/getSelectionEnd避免无关订阅并通过selectionChange派发选区变化将选区精确记录为「clientIdattributeKey即identifier offset」。这意味着一个块内多个RichText字段如标题正文各自拥有独立的选区状态这正是identifier的价值所在。预览模式与 Block Bindings预览模式PublicForwardedRichTextContainer检测到isPreviewMode上下文时会剥离全部交互属性仅渲染valueToHTMLString产出的静态 HTMLdangerouslySetInnerHTML用于不可编辑的预览场景。Block Bindings当块属性通过 bindings 绑定到外部数据源时组件会读取blockBindings、判断数据源是否允许用户编辑canUserEditValue并据此设置readOnly、占位符与提示文案实现「连接字段的只读展示或可编辑」双模式见 index.jsx 中disableBoundBlock相关逻辑。总结渲染富文本RichText tagNameh2 identifiercontent value{...} onChange{...} /其中identifier必须与块属性名一致保存富文本save函数中必须使用RichText.Content tagNameh2 value{ attributes.content } /避免把编辑态 DOM 直接序列化限制格式allowedFormats精确控制允许的格式列表withoutInteractiveFormatting剔除交互类格式扩展工具栏registerFormatTypeRichTextToolbarButton可带name与快捷键即可把自定义格式按钮注入工具栏格式名形如my-plugin/my-format注意弃用不要使用multiline与onSplit分别迁移到InnerBlocks与block.json的supports.splitting。掌握了RichText的完整属性与底层机制即可在自定义块中实现与核心块Heading、Paragraph一致的编辑体验并进一步通过RichTextToolbarButton构建属于你自己的格式能力。【免费下载链接】gutenbergThe Block Editor project for WordPress and beyond. Plugin is available from the official repository.项目地址: https://gitcode.com/GitHub_Trending/gu/gutenberg创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表