使用 PluginMoreMenuItem 为 WordPress 块编辑器注入"更多工具与选项"菜单项
【免费下载链接】gutenbergThe Block Editor project for WordPress and beyond. Plugin is available from the official repository.项目地址: https://gitcode.com/GitHub_Trending/gu/gutenberg
PluginMoreMenuItem是 Gutenberg 编辑器包(@wordpress/editor)提供的 SlotFill 组件,允许插件在块编辑器右上角"更多工具与选项"(More Tools & Options)下拉菜单中注入自定义菜单项。本文将以 docs/reference-guides/slotfills/plugin-more-menu-item.md 为骨架,结合仓库源码(packages/editor/src/components/plugin-more-menu-item/index.jsx、packages/editor/src/components/more-menu/index.jsx 等)深入讲解其用法、Props、底层原理、条件渲染与迁移注意事项,读完即可在你的插件中实现一个功能完整的菜单项。
PluginMoreMenuItem 是什么
PluginMoreMenuItem是一个 SlotFill 扩展点,其作用正如官方文档所述:
This slot will add a new item to the More Tools & Options section.
即向块编辑器界面右上角的"选项(Options)"下拉菜单(即通常所说的 More Menu)中新增一个菜单条目。该条目可以表现为按钮(执行回调)或链接(跳转 URL),具体取决于传入的 Props。
SlotFill 是 Gutenberg 暴露给开发者的扩展机制,允许把内容注入到编辑器预先定义好的位置。按照 SlotFills 参考文档 的说明,使用任何 SlotFill 都需要四步:
- 从
@wordpress/plugins导入registerPlugin; - 从
@wordpress/editor导入所需的 SlotFill 组件(这里是PluginMoreMenuItem); - 定义一个渲染自定义内容的组件,把要注入的内容包裹在 SlotFill 组件内部;
- 调用
registerPlugin注册插件。
PluginMoreMenuItem与同类的PluginSidebarMoreMenuItem、PluginBlockSettingsMenuItem、PluginDocumentSettingPanel等组件共同组成了编辑器的官方扩展体系,完整的 SlotFill 清单可参考 SlotFills Reference。
快速上手:注入一个按钮型菜单项
官方文档给出了最简示例。下面的代码注册一个插件,在 More Menu 中新增一个带图标的菜单项,点击时弹出提示框:
import { registerPlugin } from '@wordpress/plugins'; import { PluginMoreMenuItem } from '@wordpress/editor'; import { image } from '@wordpress/icons'; const MyButtonMoreMenuItemTest = () => ( <PluginMoreMenuItem icon={ image } onClick={ () => { alert( 'Button Clicked' ); } } > More Menu Item </PluginMoreMenuItem> ); registerPlugin( 'more-menu-item-test', { render: MyButtonMoreMenuItemTest } );要点拆解:
registerPlugin( 'more-menu-item-test', { render: ... } ):插件名必须全局唯一,render传入要渲染的 React 组件;<PluginMoreMenuItem>的children 即菜单项的显示文本("More Menu Item");icon传入@wordpress/icons中的 SVG 图标;onClick是点击菜单项时执行的回调。
菜单项出现的位置在编辑器右上角的竖向省略号(Options)下拉菜单中。在该菜单展开时,插件注入的条目会出现在"面板(Panels)"分组中,与编辑器内置的视图切换(Visual editor / Code editor)等条目同属一个大菜单。
核心 API 与 Props 详解
根据组件源码 JSDoc(packages/editor/src/components/plugin-more-menu-item/index.jsx#L9-L14),PluginMoreMenuItem支持以下 Props:
| Prop | 类型 | 默认值 | 说明 |
|---|---|---|---|
children | React.ReactNode | — | 菜单项的显示内容,通常为标签文本 |
href | string | — | 提供时菜单项渲染为<a>链接而非按钮,对应锚点的href属性 |
icon | Dashicon slug 字符串或 SVG WP 元素 | 继承自插件 | 渲染在菜单项标签左侧的图标 |
onClick | Function | noop | 用户点击菜单项时执行的回调函数 |
| 其他 Props | ...* | — | 透传给底层菜单项组件;as已被废弃并忽略 |
几个值得注意的行为:
icon默认继承插件图标。源码中icon={ itemProps.icon || context.icon },context来自usePluginContext()(packages/editor/src/components/plugin-more-menu-item/index.jsx#L62-L81)。因此如果你在registerPlugin时设置了插件的icon,菜单项会自动使用它,无需重复传入。href决定渲染形态。传入href时菜单项变成链接(源码中由more-menu-group.tsx的toMenuItems根据fill.props.href !== undefined选择渲染Menu.LinkItem还是Menu.Item,见 more-menu-group.tsx#L64-L72)。as已废弃。源码检测到as时调用deprecated()并忽略该属性(自版本 7.2 起),因为菜单项由菜单自身的机制渲染,不需要外部指定组件。
使用 ES5 语法的写法
源码 JSDoc 中还保留了基于wp.*全局对象的 ES5 示例,适用于未使用构建工具的环境:
var __ = wp.i18n.__; var PluginMoreMenuItem = wp.editor.PluginMoreMenuItem; var moreIcon = wp.element.createElement( 'svg' ); // ...svg element. function onButtonClick() { alert( 'Button clicked.' ); } function MyButtonMoreMenuItem() { return wp.element.createElement( PluginMoreMenuItem, { icon: moreIcon, onClick: onButtonClick, }, __( 'My button title' ) ); }现代开发建议直接使用import语法(ESNext),因为wp.editor.PluginMoreMenuItem需要你的插件在编辑器环境(@wordpress/editor已加载)中运行。
源码原理:从 PluginMoreMenuItem 到菜单渲染
要理解这个 SlotFill 的完整链路,需要沿着源码走一遍。
1. 组件本身只是 ActionItem 的封装
PluginMoreMenuItem的真实实现非常轻量(packages/editor/src/components/plugin-more-menu-item/index.jsx#L62-L81):
export default function PluginMoreMenuItem( props ) { const context = usePluginContext(); const { as, ...itemProps } = props; if ( as ) { deprecated( 'The `as` prop of wp.editor.PluginMoreMenuItem', { since: '7.2', hint: 'The menu renders the item itself. The prop is ignored.', } ); } return ( <ActionItem name="core/plugin-more-menu" icon={ itemProps.icon || context.icon } { ...itemProps } /> ); }它把一切交给@wordpress/interface包的ActionItem,并指定了 Slot 名称为core/plugin-more-menu。
2. ActionItem 通过 Slot/Fill 建立连接
ActionItem由Fill(填充方,即你的插件)与ActionItem.Slot(槽位方,即编辑器菜单)两部分组成(packages/interface/src/components/action-item/index.tsx):
ActionItem渲染<Fill name={ name }>,把自身作为填充内容注入槽位;ActionItem.Slot渲染<Slot name={ name }>,收集所有同名 Fill,通过children函数(render prop)交给菜单渲染。
ActionItem有一个关键交互设计:槽位会把"用于渲染菜单项的组件"(as,默认MenuItem)和"关闭菜单的 onClick"通过fillProps传给每个 Fill。Fill 端的onClick不会替换槽位的 handler,而是两者串联执行(action-item/index.tsx#L33-L60):
const Component = as ?? slotAs; const handlers = [ onClick, slotOnClick ].filter( Boolean ); // ...合并调用所有 handlers这意味着你写的onClick执行后,菜单还会自行关闭,无需手动处理。
3. 编辑器 More Menu 中的槽位
真正的"插槽"位于编辑器头部菜单组件中(packages/editor/src/components/more-menu/index.jsx#L114-L123):
<ActionItem.Slot name="core/plugin-more-menu" fillProps={ { as: MoreMenuItem } } > { ( items ) => ( <MoreMenuGroup label={ __( 'Panels' ) }> { items } </MoreMenuGroup> ) } </ActionItem.Slot>可以看到:
- 槽位名称
core/plugin-more-menu与PluginMoreMenuItem内部使用的名称完全一致; fillProps.as被设为编辑器的MoreMenuItem,因此你的菜单项最终由编辑器自己的菜单项组件渲染,保证风格与无障碍行为一致;- 所有插件注入的条目被统一收进 label 为Panels(面板)的分组,组内由 MoreMenuGroup 负责把每个 Fill 转换为
Menu.Item(按钮)或Menu.LinkItem(链接)。
4. 分组与键盘导航
MoreMenuGroup的toMenuItems函数(more-menu-group.tsx#L49-L73)负责把"自带渲染内容的 Fill"包装成真正参与菜单键盘导航的菜单项,并利用renderprop 保留 Fill 自身的渲染输出。也就是说,即使你的组件没有使用编辑器提供的MoreMenuItem,它也会被正确包装进菜单的无障碍导航树中。
进阶实践:链接菜单项与条件渲染
渲染为链接
需要跳转时传入href,菜单项即变为链接:
import { registerPlugin } from '@wordpress/plugins'; import { PluginMoreMenuItem } from '@wordpress/editor'; import { external } from '@wordpress/icons'; const MyLinkMoreMenuItem = () => ( <PluginMoreMenuItem href="https://example.com/docs" icon={ external } target="_blank" > View documentation </PluginMoreMenuItem> ); registerPlugin( 'more-menu-item-link', { render: MyLinkMoreMenuItem } );href之外的其他 Props(如target)会透传给底层菜单项组件。菜单对链接型条目会渲染为Menu.LinkItem,支持target="_blank"新窗口打开。
控制注入时机与范围
根据 SlotFills 参考文档 的说明,绝大多数 SlotFill(包括PluginMoreMenuItem)在文章编辑器(Post Editor)与站点编辑器(Site Editor)中都会渲染,因此通常需要自行控制显示范围。常用手法包括:
- 限制在文章编辑器:通过
editorStore.getCurrentPostType()与coreStore.getPostType()判断当前文章类型的viewable属性是否为true; - 限制在特定文章类型:维护一个允许列表,如
[ 'page' ],仅在该列表内渲染; - 限制在站点编辑器:与上面逻辑相反,非 viewable 的内部文章类型(如
wp_template、wp_template_part、wp_block)即站点编辑器场景。
示例(仅对 page 文章类型显示菜单项):
import { registerPlugin } from '@wordpress/plugins'; import { PluginMoreMenuItem, store as editorStore } from '@wordpress/editor'; import { store as coreStore } from '@wordpress/core-data'; import { useSelect } from '@wordpress/data'; import { settings } from '@wordpress/icons'; const PageOnlyMoreMenuItem = () => { const isPage = useSelect( ( select ) => { const postType = select( editorStore ).getCurrentPostType(); return postType === 'page'; }, [] ); if ( ! isPage ) { return null; } return ( <PluginMoreMenuItem icon={ settings }> Page-only action </PluginMoreMenuItem> ); }; registerPlugin( 'page-only-more-menu-item', { render: PageOnlyMoreMenuItem } );结合其他 SlotFill 使用
PluginMoreMenuItem常与PluginSidebar配合:通过PluginSidebarMoreMenuItem打开侧边栏。若你的侧边栏开关已经存在于"更多工具与选项"菜单中,注意不要让菜单项重复;PluginSidebarMoreMenuItem内部同样基于core/plugin-more-menu槽位机制工作,属于同一体系(相关组件清单见 packages/editor/src/components/index.js#L28)。
兼容性与迁移说明
历史上PluginMoreMenuItem同时从@wordpress/edit-post暴露(wp.editPost.PluginMoreMenuItem)。在 packages/edit-post/src/deprecated.jsx 中可以确认,该别名自6.6起已废弃:
const deprecateSlot = ( name ) => { deprecated( `wp.editPost.${ name }`, { since: '6.6', alternative: `wp.editor.${ name }`, } ); };并且wp.editPost版本的PluginMoreMenuItem在站点编辑器(site-editor.php)中会直接返回null(deprecated.jsx#L17-L19、deprecated.jsx#L54-L60)。因此:
- 新代码一律从
@wordpress/editor导入PluginMoreMenuItem; - 旧代码应迁移
wp.editPost.PluginMoreMenuItem→wp.editor.PluginMoreMenuItem; - 从
@wordpress/edit-site暴露的同名别名同样是迁移桩(见 packages/edit-site/src/deprecated.jsx#L24-L29),同样应改用@wordpress/editor。
无障碍与测试保障
菜单项最终由@wordpress/ui的Menu体系渲染,自带完整的键盘导航、ARIA 角色与焦点管理。仓库中的单元测试(packages/editor/src/components/more-menu/test/more-menu-item.jsdom.test.tsx)覆盖了以下关键行为,可作为你实现菜单项时的参考基准:
- 复选框/单选角色保留:
role="menuitemcheckbox"、menuitemradio与aria-checked状态会被正确保留,isSelected也能驱动选中态; - 链接渲染:
href空字符串也渲染为链接;target="_blank"生效;disabled链接变为不可交互项并带aria-disabled="true"; - 快捷键显示:支持字符串或
{ display, ariaLabel }对象形式的shortcut,并提供可访问的快捷键描述; - Dashicon:以 slug 传入的图标(如
"editor-kitchensink")会渲染对应的.dashicons类名。
小结
PluginMoreMenuItem是一个"薄封装 + 强生态"的扩展点:组件本身只有约 20 行代码,但经由ActionItem(packages/interface/src/components/action-item/index.tsx)与core/plugin-more-menu槽位,被无缝接入编辑器右上角"更多工具与选项"菜单的 Panels 分组,并获得图标继承、链接/按钮双形态、菜单关闭联动与无障碍键盘导航等能力。
实际开发时只需记住:从@wordpress/editor导入、用registerPlugin注册、用icon/href/onClick/children 控制形态,并按需用editorStore/coreStore做条件渲染。若想了解更多注入点,可继续阅读 SlotFills Reference 中列出的其他 SlotFill 文档,或查看 @wordpress/plugins 包说明 了解插件注册 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),仅供参考