Gutenberg 命令面板(Command Palette)开发指南:@wordpress/commands 包的静态命令、动态加载器与上下文机制

Gutenberg 命令面板(Command Palette)开发指南:@wordpress/commands 包的静态命令、动态加载器与上下文机制 Gutenberg 命令面板Command Palette开发指南wordpress/commands 包的静态命令、动态加载器与上下文机制【免费下载链接】gutenbergThe Block Editor project for WordPress and beyond. Plugin is available from the official repository.项目地址: https://gitcode.com/GitHub_Trending/gu/gutenberg导读wordpress/commands是 GutenbergWordPress 块编辑器项目中用于构建命令面板Command Palette的通用包它允许开发者注册、修改并以统一的命令菜单形式展示各类操作。本文以该包的官方文档为主体结合仓库内 store、hooks 与 components 的源码实现系统讲解命令注册的两种方式静态与动态、命令对象结构、上下文context与分类category机制以及如何使用 WordPress Data API 编程式控制面板开关。阅读完本文你将掌握在编辑器或站点编辑器中注册自有命令、实现随搜索词实时变化的动态命令、并利用上下文命令提升特定场景操作效率的完整实战方案。一、命令面板是什么命令面板Command Palette是一个统一的命令搜索与执行入口在编辑器中按下cmdkWindows/Linux 下为CtrlK即可唤起。它把散落在编辑器各处的操作——新建页面、打开偏好设置、切换模板、编辑文档等——聚合到一个可搜索、可键盘操作的面板中与 VS Code 等现代编辑器的 Command Palette 体验一致。在仓库的 components/command-menu.jsx 中可以看到面板的快捷键core/commands被注册为category: global、键位为modifier: primarycharacter: k的全局快捷键触发时根据面板当前开关状态执行close()或open()并额外通过withIgnoreIMEEvents忽略输入法IME组合事件避免中文等输入法场景下的误触发registerShortcut( { name: core/commands, category: global, description: __( Open the command palette. ), keyCombination: { modifier: primary, character: k }, } );面板本体是一个基于cmdk库的模态框Modal包含搜索输入框、Recent最近使用、Suggestions上下文建议、Results搜索结果等多个分组将在下文渲染与交互实现一节详述。二、命令的两种注册方式静态与动态所有命令注册 API 都接收一个命令对象command object其完整字段如下字段类型必填说明namestring是机器可读的唯一命令名建议使用plugin/command-name命名空间前缀labelstring是在面板中展示给人看的可读文本一般用__()做国际化iconSVG 图标 / 元素 / 函数是命令左侧的 SVG 图标可从wordpress/icons引入callbackFunction是命令被选中时执行的回调接收{ close }等参数categorystring否命令分类见下文命令分类缺省或非法时回退为actioncontextstring否命令生效的上下文见下文上下文命令keywordsstring[]否用于搜索匹配的附加关键词数组searchLabelstring否搜索时使用的独立标签区别于展示用labeldisabledboolean否为true时不注册该命令补充searchLabel与disabled虽未在 README 属性表中列出但在 store/actions.js 的WPCommandConfig类型定义与 reducer 的字段落库逻辑中均真实存在属于可直接使用的完整配置项。2.1 静态命令Static commands静态命令适用于动作固定、不依赖搜索词的场景例如新增页面、打开编辑器偏好设置弹窗等。有两种注册方式方式一React HookuseCommand推荐在 React 组件内使用import { useCommand } from wordpress/commands; import { plus } from wordpress/icons; useCommand( { name: myplugin/my-command-name, label: __( Add new post ), icon: plus, category: command, callback: ( { close } ) { document.location.href post-new.php; close(); }, } );从 hooks/use-command.js 的实现看useCommand本质上是registerCommand/unregisterCommand两个 data action 的封装组件挂载时通过useEffect注册命令卸载时自动注销return () unregisterCommand( command.name )无需手动清理command.disabled为true时直接跳过注册。回调通过useRef保存最新引用避免因闭包捕获过期回调。方式二Data 派发wp.data.dispatch(...).registerCommandimport { store as commandsStore } from wordpress/commands; wp.data.dispatch( commandsStore ).registerCommand( { name: myplugin/open-preferences, label: __( Open Editor Preferences ), icon: settings, category: view, callback: ( { close } ) { openPreferences(); close(); }, } );该方法与 Hook 等价适合在非 React 上下文如现有 JS 初始化代码中使用。与之配对的unregisterCommand( name )用于移除命令对应 action 实现在 store/actions.js。批量注册多个静态命令可使用useCommandsHook传入命令对象数组。useCommands为每个命令注册独立的条目并在依赖数组变化或卸载时统一注销适用于一个插件一次性暴露一组相关操作的场景完整示例见 hooks/use-command.js。2.2 动态命令Dynamic commands与命令加载器当命令列表依赖于用户在面板输入框中敲入的搜索词或仅在特定条件下才可用时静态注册不再适用。此时需要命令加载器command loader通过useCommandLoaderHook 注册一个自定义 React Hook该 Hook 接收{ search }参数并返回动态生成的命令数组。文档给出的典型场景是用户输入 contact 时面板需要按该输入去过滤页面记录尝试找到 Contact 页面。完整示例页面搜索加载器如下import { __ } from wordpress/i18n; import { addQueryArgs } from wordpress/url; import { useCommandLoader } from wordpress/commands; import { page } from wordpress/icons; import { useSelect } from wordpress/data; import { store as coreStore } from wordpress/core-data; import { useMemo } from wordpress/element; function usePageSearchCommandLoader( { search } ) { // 依据 search 词检索页面。 const { records, isLoading } useSelect( ( select ) { const { getEntityRecords } select( coreStore ); const query { search: !! search ? search : undefined, per_page: 10, orderby: search ? relevance : date, }; return { records: getEntityRecords( postType, page, query ), isLoading: ! select( coreStore ).hasFinishedResolution( getEntityRecords, [ postType, page, query ] ), }; }, [ search ] ); // 生成命令列表。 const commands useMemo( () { return ( records ?? [] ).slice( 0, 10 ).map( ( record ) { return { name: record.title?.rendered record.id, label: record.title?.rendered ? record.title?.rendered : __( (no title) ), icon: page, category: edit, callback: ( { close } ) { const args { p: /page, postId: record.id, }; document.location addQueryArgs( site-editor.php, args ); close(); }, }; } ); }, [ records ] ); return { commands, isLoading, }; } useCommandLoader( { name: myplugin/page-search, hook: usePageSearchCommandLoader, } );示例中的关键点加载器 Hook 必须返回{ commands, isLoading }其中commands为满足条件的命令数组isLoading告知面板当前是否仍在异步获取数据如等待 REST 请求返回。查询参数search为空时传undefined此时orderby回退为date即未输入时展示最近页面输入后才按相关性排序——这是面板默认展示内容的常见策略。useMemo以[ records ]为依赖缓存命令列表避免每次渲染都重建。命令name由标题与 ID 拼接record.title?.rendered record.id保证唯一性。isLoading通过hasFinishedResolution取反获得与 core-data 的实体请求解析状态联动。从 hooks/use-command-loader.js 的实现可以看到两个细节加载器的hook被包在一个稳定引用useCallback空依赖中真正执行业务逻辑的 hook 保存在currentHookRef中——因此面板总是调用最新的 hook但更换 hook 实例不会触发面板重渲染、也不会重复注册加载器。与useCommand相同组件卸载时自动执行unregisterCommandLoader( loader.name )清理且disabled为true时跳过注册。同样地动态命令也可通过 Data 层注册wp.data.dispatch( commandsStore ).registerCommandLoader( { name, hook, context, category } )对应 action 见 store/actions.js。三、上下文命令Contextual commands静态命令与动态命令都可以声明为上下文命令在特定上下文例如处于站点编辑器导航中、正在编辑某个模板下这些命令在打开面板时优先显示并且在输入搜索时排在其他命令之前。当前已实现三种上下文可在 hooks/use-command-context.js 与文档中对应确认上下文值触发场景site-editor正在站点编辑器Site Editor中导航侧边栏可见entity-edit正在编辑某个文档实体模板、模板部件或页面block-selection-edit有块被选中时给命令挂载上下文只需在useCommand/useCommandLoader的配置中加上context属性useCommand( { name: myplugin/template-actions, label: __( Template actions ), icon: layout, context: entity-edit, // 仅编辑实体时高优先级显示 category: command, callback: ( { close } ) { /* ... */ close(); }, } );底层实现上下文由 store 维护reducer 中context状态默认值为root见 store/reducer.js仅能通过私有 actionsetContext修改。useCommandContext( context )Hookuse-command-context.js负责在组件挂载时设置上下文、卸载时恢复进入前的上下文实现进入站点编辑器→设置site-editor上下文→离开后还原的自动管理。Selector 层面getCommands( contextual )与getCommandLoaders( contextual )接受布尔参数传true只返回与当前上下文匹配的命令传false默认返回其余命令见 store/selectors.js。面板渲染时正是据此把上下文命令单独取出、优先展示见下文Suggestions 与 Results 分组。四、命令分类Command categoriescategory用于描述命令执行的动作类型面板据此对命令做视觉区分在命令项右侧显示分类标签。可用分类如下分类含义典型例子command执行代码或切换状态添加块、复制块view导航到后台某区域或打开面板前往模板edit导航去编辑某个文档编辑模板、编辑页面action其他分类都不匹配时的通用回退传入非法分类时也默认回退到此兜底默认值与校验不指定category时命令自动为action。在 store/actions.js 中定义了可注册分类集合const REGISTERABLE_CATEGORIES new Set( [ command, view, edit, action ] );registerCommand与registerCommandLoader都会执行校验分类不存在于集合中时静默回退为action源码注释注明未来版本将补充 development 模式下的警告输出。此外源码还预留了workflow分类面板侧 command-menu.jsx 的CATEGORY_LABELS中包含workflow: __( Workflow )但它仅供内部保留使用不允许通过公开 API 注册。分类回退图标分类还可提供回退图标——当命令未传自己的icon时使用。目前仅view分类定义了回退图标arrowRight右箭头见 command-menu.jsx使得导航到某处类命令在整个面板中视觉语义一致。该分类下的命令被期望依赖回退图标而非自行传图除非自身图标能表达箭头之外的额外信息例如在新标签页打开。五、WordPress Data API编程式控制命令面板命令面板的状态托管在名为core/commands的 Redux store 中STORE_NAME core/commands见 store/index.js可通过 WordPress Data API 读写。官方数据文档提供 selectors 与 actions 的完整清单此处结合本仓库源码给出核心成员Selectorsstore/selectors.jsSelector说明getCommands( contextual false )获取已注册的静态命令列表contextualtrue时仅返回当前上下文命中的命令getCommandLoaders( contextual false )获取已注册的命令加载器列表同上isOpen()命令面板当前是否打开getContext()当前激活的上下文root/site-editor/entity-edit/block-selection-editActionsstore/actions.jsAction说明open()编程式打开命令面板close()编程式关闭命令面板registerCommand( config )/unregisterCommand( name )注册 / 注销静态命令registerCommandLoader( config )/unregisterCommandLoader( name )注册 / 注销命令加载器用法示例如文档 API 部分所示import { store as commandsStore } from wordpress/commands; import { useDispatch } from wordpress/data; // 在组件内通过 Hook 打开命令中心 const { open: openCommandCenter } useDispatch( commandsStore ); // 或在任意 JS 中直接派发 wp.data.dispatch( commandsStore ).open(); wp.data.select( commandsStore ).isOpen(); // true / falsestore 的创建与注册见 store/index.js通过createReduxStore构建、register全局注册并用unlock( store ).registerPrivateActions / registerPrivateSelectors挂载私有 APIsetContext、setLoaderLoading、isLoading等私有成员需借助wordpress/private-apis的 unlock 机制访问。六、安装与样式引入6.1 安装npm install wordpress/commands --save环境要求该包假设运行环境为ES2015。若你的目标环境对这类语言特性与 API 支持有限或完全不支持应在代码中引入wordpress/babel-preset-default提供的 polyfill参见仓库内 packages/babel-preset-default 的 polyfill 说明。版本与依赖当前仓库中该包版本为1.55.0Node 要求18.12.0、npm8.19.2React 18 / 19 均作为 peer 依赖支持见 packages/commands/package.json。核心运行依赖包括wordpress/components、wordpress/data、wordpress/i18n、wordpress/icons、wordpress/keyboard-shortcuts、wordpress/keycodes、wordpress/preferences以及面板 UI 底层库cmdk。6.2 样式引入为保证命令面板正常显示需要引入以下样式表node_modules内/* From node_modules: */ import wordpress/components/build-style/style.css; import wordpress/commands/build-style/style.css;第一行引入底层组件库Modal、HStack、TextHighlight 等样式第二行引入命令面板自身样式。本包自身的 SCSS 源文件位于 packages/commands/src/style.scss 与 packages/commands/src/components/style.scss。七、渲染与交互实现面板内部工作机制从组件源码components/command-menu.jsx可以完整还原面板的渲染流程有助于理解文档所述各机制的实际落地命令项渲染CommandItem根据命令的category选择图标command.icon ?? CATEGORY_FALLBACK_ICONS[ category ]用TextHighlight对label做搜索词高亮并在右侧渲染CATEGORY_LABELS[ category ]分类标签keywords数组会注入cmdk的匹配逻辑用于辅助搜索。选中命令时调用recordUsage( command.name )记录使用痕迹供最近使用分组排序随后执行command.callback( { close } )。三种分组策略RecentGroup未输入搜索词时展示最近使用过的命令是提升高频操作效率的关键交互SuggestionsGroup未输入搜索词时通过getCommands( true )/getCommandLoaders( true )拉取当前上下文的命令作为建议项——这就是上下文命令优先可见的渲染落点ResultsGroup输入搜索词后先渲染常规结果getCommands( false )再把上下文命令getCommands( true )追加在后面实现文档所述上下文命令排在其他命令之上。空状态搜索非空且所有 loader 加载完毕loadersLoading为假但仍无结果时展示 No results found.。加载器渲染每个 loader 的hook被CommandMenuLoaderWrapper以 key 强制的重挂载方式调用避免违反 React Hooks 规则isLoading状态同步进 store 的loaderStates供空状态判断使用。八、小结与延伸阅读wordpress/commands以静态命令 动态加载器双轨注册模型配合上下文优先级与分类视觉体系为编辑器提供了一个可无限扩展的键盘驱动命令入口。无论是注册一个固定动作、实现按搜索词实时过滤的实体命令还是把面板能力包装成语义清晰的分类都可以在本文示例的基础上直接落地。想深入底层建议继续阅读本仓库内以下文件官方包文档packages/commands/README.md数据层actions 与 selectors 定义 packages/commands/src/store/actions.js、packages/commands/src/store/selectors.js、状态归约 packages/commands/src/store/reducer.jsHooks 层packages/commands/src/hooks/use-command.js、packages/commands/src/hooks/use-command-loader.js、packages/commands/src/hooks/use-command-context.js面板组件packages/commands/src/components/command-menu.jsx包元信息与依赖packages/commands/package.jsonRedux store 机制参考packages/data/README.md私有 API 解锁机制参考packages/private-apis【免费下载链接】gutenbergThe Block Editor project for WordPress and beyond. Plugin is available from the official repository.项目地址: https://gitcode.com/GitHub_Trending/gu/gutenberg创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考