资讯详情

Gutenberg 富文本数据 Store 完全指南:core/rich-text 命名空间的 Selectors 与 Format Type 注册机制

📅 2026/9/16 17:42:30 | 华诺云谱 👁 阅读
Gutenberg 富文本数据 Store 完全指南:core/rich-text 命名空间的 Selectors 与 Format Type 注册机制
Gutenberg 富文本数据 Store 完全指南core/rich-text 命名空间的 Selectors 与 Format Type 注册机制【免费下载链接】gutenbergThe Block Editor project for WordPress and beyond. Plugin is available from the official repository.项目地址: https://gitcode.com/GitHub_Trending/gu/gutenbergcore/rich-text是 Gutenberg 项目中管理富文本格式化类型Format Type的专用数据 Store。本文以>import { createReduxStore, register } from wordpress/data; import reducer from ./reducer; import * as selectors from ./selectors; import * as actions from ./actions; const STORE_NAME core/rich-text; export const store createReduxStore( STORE_NAME, { reducer, selectors, actions, } ); register( store );关键信息命名空间Namespacecore/rich-text是外部访问该 Store 的唯一标识。导出方式包入口同时导出了store定义对象因此组件代码中可以用store as richTextStore的方式从wordpress/rich-text引入再交给useSelect( ( select ) select( richTextStore ) )使用避免手写字符串core/rich-text造成拼写错误。职责边界该 Store 只维护一类状态——已注册的格式类型format types集合它并不保存文档正文内容正文内容由RichTextValue对象含text、formats、start、end四个字段详见 README.md表示。二、Selectors 详解原文档共公开 4 个 Selector均从 selectors.js 自动生成 API 文档。下面逐一讲解其签名、用途、使用示例与实现要点。1. getFormatType按名称返回指定的格式类型对象。签名getFormatType( state: Object, name: string ): ?Objectstate数据状态由useSelect自动注入无需手动传递name格式类型的名称例如core/bold返回匹配的格式类型对象未找到时返回undefined。原文档完整示例在 React 组件中查询core/bold并列出其全部属性import { __, sprintf } from wordpress/i18n; import { store as richTextStore } from wordpress/rich-text; import { useSelect } from wordpress/data; const ExampleComponent () { const { getFormatType } useSelect( ( select ) select( richTextStore ), [] ); const boldFormat getFormatType( core/bold ); return boldFormat ? ( ul { Object.entries( boldFormat )?.map( ( [ key, value ] ) ( li { key } : { value } /li ) ) } /ul ) : ( __( Not Found ) ; };源码实现selectors.js#L77-L79export function getFormatType( state, name ) { return state.formatTypes[ name ]; }即直接从state.formatTypes对象上按键名取值。由于格式类型在注册时就是以name为键存入状态的见下文 reducer 一节该 Selector 是 O(1) 复杂度、无缓存开销的简单读取。2. getFormatTypeForBareElement根据元素的标签名tag name查找可处理该裸元素bare element即不带data-format-type属性的元素的格式类型。签名getFormatTypeForBareElement( state: Object, bareElementTagName: string ): ?ObjectbareElementTagName元素标签名例如strong、a返回匹配的格式类型对象找不到时返回undefined。原文档完整示例传入strong查询对应的格式并渲染其名称import { __, sprintf } from wordpress/i18n; import { store as richTextStore } from wordpress/rich-text; import { useSelect } from wordpress/data; const ExampleComponent () { const { getFormatTypeForBareElement } useSelect( ( select ) select( richTextStore ), [] ); const format getFormatTypeForBareElement( strong ); return format p{ sprintf( __( Format name: %s ), format.name ) }/p; };源码实现selectors.js#L109-L119export function getFormatTypeForBareElement( state, bareElementTagName ) { const formatTypes getFormatTypes( state ); return ( formatTypes.find( ( { className, tagName } ) { return className null bareElementTagName tagName; } ) || formatTypes.find( ( { className, tagName } ) { return className null * tagName; } ) ); }匹配规则两级回退优先查找className null即纯标签型格式且tagName与目标标签完全一致的格式类型若第一步未命中则回退查找tagName *的通配符格式——例如core/unknown这类可兜底处理任意标签的格式。关键前提只有注册时把className显式设为null的格式才会被当作 bare 格式参与匹配带 class 的格式会被排除这一点在单元测试中特意做了顺序性验证见下文测试章节。3. getFormatTypeForClassName根据元素的CSS 类名查找可以处理该元素的格式类型。签名getFormatTypeForClassName( state: Object, elementClassName: string ): ?ObjectelementClassName元素上的类名字符串可包含多个以空格分隔的类返回匹配的格式类型对象找不到时返回undefined。原文档完整示例传入has-inline-color查询内联颜色格式import { __, sprintf } from wordpress/i18n; import { store as richTextStore } from wordpress/rich-text; import { useSelect } from wordpress/data; const ExampleComponent () { const { getFormatTypeForClassName } useSelect( ( select ) select( richTextStore ), [] ); const format getFormatTypeForClassName( has-inline-color ); return format p{ sprintf( __( Format name: %s ), format.name ) }/p; };源码实现selectors.js#L148-L155export function getFormatTypeForClassName( state, elementClassName ) { return getFormatTypes( state ).find( ( { className } ) { if ( className null ) { return false; } return ${ elementClassName } .indexOf( ${ className } ) 0; } ); }匹配细节className null的 bare 格式一律不参与类名匹配直接返回false匹配采用两侧补空格再indexOf的技巧即 has-inline-color .indexOf( has-inline-color )这样可以精确匹配完整类名避免把has-inline-color-dark之类包含前缀的类误判为命中该方法按getFormatTypes()返回的顺序从前向后查找返回第一个命中的格式类型。4. getFormatTypes返回当前 Store 中全部已注册的格式类型数组。签名getFormatTypes( state: Object ): Array返回格式类型对象数组没有任何注册时为空数组。原文档完整示例列出所有可用格式的名称import { __, sprintf } from wordpress/i18n; import { store as richTextStore } from wordpress/rich-text; import { useSelect } from wordpress/data; const ExampleComponent () { const { getFormatTypes } useSelect( ( select ) select( richTextStore ), [] ); const availableFormats getFormatTypes(); return availableFormats ? ( ul { availableFormats?.map( ( format ) ( li{ format.name }/li ) ) } /ul ) : ( __( No Formats available ) ); };源码实现selectors.js#L36-L39export const getFormatTypes createSelector( ( state ) Object.values( state.formatTypes ), ( state ) [ state.formatTypes ] );这是 4 个 Selector 中唯一使用createSelector构造的。它依赖wordpress/data的createSelector做记忆化缓存只要state.formatTypes引用没有变化就会复用上次的数组结果避免每次调用都重新Object.values分配新数组同时它也是另外两个查找型 SelectorgetFormatTypeForBareElement、getFormatTypeForClassName共用的数据源。三、Actions为什么文档显示 Nothing to document原文档的 Actions 一节内容为Nothing to document.这并非 Store 没有 action而是有意为之。查看 actions.js 源码该 Store 实际定义了两个 action creatoraddFormatTypes( formatTypes )派发ADD_FORMAT_TYPES将单个或一组格式类型合并进状态removeFormatTypes( names )派发REMOVE_FORMAT_TYPES按名称列表从状态中移除格式。两者的源码注释都标注了ignore并明确说明对外暴露的 API 是registerFormatType与unregisterFormatType来自wordpress/rich-text包开发者不应直接派发这两个 action而应使用包级函数因为它们内部自带完整的校验逻辑。这也是data-core-rich-text.md文档中 Actions 部分留空、而 Selectors 部分保留完整文档的原因——状态读取是公开查询能力而状态写入被收敛为受控的注册 API。四、底层原理formatTypes 状态与 reducercore/rich-text的状态结构非常简单仅由一个formatTypes字段组成reducer 定义于 reducer.jsexport function formatTypes( state {}, action ) { switch ( action.type ) { case ADD_FORMAT_TYPES: return { ...state, // Key format types by their name. ...action.formatTypes.reduce( ( newFormatTypes, type ) ( { ...newFormatTypes, [ type.name ]: type, } ), {} ), }; case REMOVE_FORMAT_TYPES: return Object.fromEntries( Object.entries( state ).filter( ( [ key ] ) ! action.names.includes( key ) ) ); } return state; } export default combineReducers( { formatTypes } );可以总结出三个实现要点以 name 为键的对象字典状态是{ [formatName]: formatType }形式的普通对象这正是getFormatType可以直接按键取值、getFormatTypes通过Object.values取数组的原因不可变性ADD_FORMAT_TYPES通过展开运算符生成新对象REMOVE_FORMAT_TYPES通过过滤重建对象保证每次状态更新都产生新引用从而让getFormatTypes的记忆化缓存依赖引用比较能够正确失效与复用支持批量操作addFormatTypes接受数组或单个对象removeFormatTypes接受字符串或数组方便批量注册/注销。五、实战链路从注册到查询的完整闭环1. 注册格式类型registerFormatType 及其校验规则所有查询的前提是格式已注册。核心格式如core/bold、core/italic、core/link等由 format-library/src/index.ts 统一注册import { registerFormatType } from wordpress/rich-text; import formats from ./default-formats; formats.forEach( ( { name, ...settings } ) registerFormatType( name, settings ) );registerFormatType( name, settings )定义于 register-format-type.js其内部在派发addFormatTypes之前会执行一连串严格校验不通过则console.error并中止注册校验项规则说明名称类型name必须是字符串否则拒绝注册名称格式必须匹配/^[a-z][a-z0-9-]*\/[a-z][a-z0-9-]*$/要求命名空间前缀 斜杠 名称仅允许小写字母、数字与连字符且以字母开头例如my-plugin/my-custom-format名称唯一性通过getFormatType查询若已存在则拒绝防止重复注册tagName必须是非空字符串格式必须指定要包裹选区的 HTML 标签className必须是合法字符串或显式为nullnull表示该格式处理裸元素字符串须匹配/^[_a-zA-Z][a-zA-Z0-9_-]*$/字母开头可含连字符、下划线、字母、数字冲突检测裸格式检查getFormatTypeForBareElement类格式检查getFormatTypeForClassName同一个 tagName/className 不允许被两个格式抢占core/unknown特例除外title必须存在且为非空字符串格式的显示名称keywords最多 3 个超出上限拒绝注册注册成功后返回完整的格式类型对象name会被注入到settings中。WPFormat的完整结构定义在同一文件的 JSDoc 中包含name、tagName、interactive、object、className、title、edit等字段。2. 包级查询封装除了在组件里用useSelect走 Storewordpress/rich-text还在包级提供了两个便捷函数它们内部就是对 Store selector 的转发get-format-type.jsgetFormatType( name )→select( richTextStore ).getFormatType( name )get-format-types.jsgetFormatTypes()→select( richTextStore ).getFormatTypes()。因此非 React 环境如普通脚本也可以直接import { getFormatTypes } from wordpress/rich-text读取注册表。3. 注销格式类型unregisterFormatType与注册对称unregister-format-type.js 先通过getFormatType( name )校验格式是否存在不存在则报错再派发removeFormatTypes( name )最后返回被移除的旧格式对象。4. 完整数据流回顾从源码结构看一次注册 → 查询的完整链路为registerFormatType(name, settings) → 系列校验命名规范/唯一性/tagName/className/title... → dispatch( richTextStore ).addFormatTypes( settings ) → reducer 以 [name] 为键合并进 state.formatTypes → getFormatType / getFormatTypes / getFormatTypeForBareElement / getFormatTypeForClassName 读取 state.formatTypes 并返回结果六、测试验证Selector 行为的可复现依据仓库为这 4 个 Selector 编写了完整的单元测试位于 store/test/selectors.js测试数据构造了三种典型格式const formatType { name: core/test-format, className: null, tagName: format }; const formatTypeClassName { name: core/test-format-class-name, className: class-name, tagName: strong }; const formatTypeBareTag { name: core/test-format-bare-tag, className: null, tagName: strong };测试覆盖的断言要点getFormatTypes返回全部 3 个格式以数组形式getFormatType( core/test-format )精确返回对应对象getFormatTypeForBareElement( strong )返回formatTypeBareTag而非formatTypeClassName——测试注释特意强调顺序很重要带 className 的格式core/test-format-class-name虽然 tagName 也是strong但因className不为null绝不能被当作 bare 格式命中getFormatTypeForClassName( class-name )返回formatTypeClassName与 bare 格式互不干扰。这套测试用例可直接作为理解裸元素匹配 vs 类名匹配两条路线的行为规范也为自定义格式开发提供了可对照的预期。七、小结core/rich-text虽然只管理格式类型注册表这一份状态却是 Gutenberg 富文本格式化体系的地基getFormatType与getFormatTypes提供基础读写getFormatTypeForBareElement与getFormatTypeForClassName分别支撑纯标签与类名驱动两种元素解析策略。结合 register-format-type.js 的严格校验与 reducer.js 的不可变状态管理开发者可以安全地通过registerFormatType注册自定义格式再通过本文讲解的 4 个 Selector 在编辑界面中查询与渲染格式信息——这正是 Format API如core/bold、core/link等在编辑器工具栏中得以工作的底层机制。【免费下载链接】gutenbergThe Block Editor project for WordPress and beyond. Plugin is available from the official repository.项目地址: https://gitcode.com/GitHub_Trending/gu/gutenberg创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。