Gutenberg 编辑后管理包实战指南:深入解析 @wordpress/edit-post 与文章编辑器 UI 扩展
Gutenberg 编辑后管理包实战指南深入解析 wordpress/edit-post 与文章编辑器 UI 扩展【免费下载链接】gutenbergThe Block Editor project for WordPress and beyond. Plugin is available from the official repository.项目地址: https://gitcode.com/GitHub_Trending/gu/gutenbergwordpress/edit-post是 WordPress Gutenberg 项目中负责文章编辑场景的专属模块——它承载了经典文章编辑器Post Editor的初始化、布局与 UI 扩展能力。本文将带你完整掌握它的安装方式、initializeEditor初始化流程、core/edit-post数据仓库store以及如何借助registerPlugin与PluginSidebar等组件在编辑器内落地自定义侧边栏和设置面板。读完本文你将能够理解wordpress/edit-post与wordpress/editor的职责划分与迁移关系掌握wp.editPost全局对象与脚本依赖的注册方法基于插件 API 从零搭建一个读写post_meta的侧边栏插件并了解各扩展点组件PluginDocumentSettingPanel、PluginBlockSettingsMenuItem、PluginMoreMenuItem等的完整参数与适用场景。本文以仓库中 packages/edit-post/README.md 为骨架结合其源码、wordpress/plugins与wordpress/editor包文档以及仓库内实战教程整理而成。一、Edit Post 模块是什么wordpress/edit-post是 Gutenberg monorepo当前工作目录即仓库根目录中的一个独立包官方定位为Edit Post Module for WordPress即专门面向 WordPress 文章编辑页面post.php下的块编辑器的模块。它不负责抽象的编辑器内核而是把编辑一篇文章这一具体业务场景组装起来初始化编辑器实例、配置编辑器偏好preferences、注册核心块、管理侧边栏与 Meta Box 等。其 package.json 中记录的版本为8.55.0依赖了大量同仓库包wordpress/editor、wordpress/block-editor、wordpress/block-library、wordpress/plugins、wordpress/data、wordpress/components、wordpress/preferences等整体呈现薄壳 组装的架构。需要注意 README 中的一句关键声明This package is meant to be used only with WordPress core. Feel free to use it in your own project but please keep in mind that it might never get fully documented.也就是说该包主要面向 WordPress 核心使用虽然允许在自有项目中使用但其文档与 API 稳定度并不像wordpress/editor那样有完整保障。对插件开发者而言推荐直接使用wordpress/editor暴露的组件与数据 store这与本包自身的演进方向一致见下文废弃迁移章节。与 wordpress/editor 的职责划分在 Gutenberg 中职责被刻意分层wordpress/editor抽象WordPress 文章概念提供块解析、文章加载与保存、PluginSidebar等所有扩展组件不关心具体渲染在哪个 WordPress 屏幕中wordpress/edit-post在wordpress/editor之上做文章编辑场景的组装与入口初始化通过 src/index.jsx 对外暴露initializeEditor、store以及一组对wordpress/editor组件的现已废弃的再导出。二、安装与运行前提在支持 npm 的 JavaScript 项目中按 README 安装npm install wordpress/edit-post安装后包入口按环境区分CommonJS 环境使用build/index.cjsESM 环境使用build-module/index.mjs见 package.json 的exports字段。ES2015 运行环境要求该包默认你的代码运行在支持 ES2015 语法与 API 的环境。如果目标环境对这类语言特性支持有限或完全不支持需要在代码中引入wordpress/babel-preset-default附带的 polyfill。脚本依赖与全局对象当你在 WordPress 中通过wp_enqueue_script引入脚本时若声明wp-edit-post作为脚本依赖则该包导出的组件会暴露在全局变量wp.editPost上。这是 README 中明确给出的集成方式They can be found in the global variablewp.editPostwhen definingwp-edit-postas a script dependency.由于该包面向 WordPress core实际在 WordPress 页面里通常不需要手动入队wp-edit-post脚本——核心已加载。插件开发者更常用的是wp.editor来自wordpress/editor与wp.plugins来自wordpress/plugins具体见下文扩展实践。三、API 全景从初始化到扩展README 的 API 章节由自动生成标记START/END TOKEN(Autogenerated API docs)包裹包含两类成员编辑器生命周期函数initializeEditor、reinitializeEditor与数据仓库store以及一组Related到wordpress/editor的插槽组件。下面逐一展开并结合源码说明。3.1 initializeEditor编辑器实例的启动入口initializeEditor用于初始化并返回一个 Editor 实例其签名与参数如下参数类型说明idstring编辑器实例的唯一标识符实际用作document.getElementById( id )的容器 idpostTypestring待编辑文章的 post typepostIdObject待编辑文章的 IDsettings?Object编辑器设置对象可为空initialEditsObject初始的编程式编辑内容视为非用户发起可绕过未保存更改提示在 src/index.jsx 的实现中该函数完成了一系列关键装配步骤理解它们有助于把握整个编辑器的启动脉络查找容器并创建 React Rootconst target document.getElementById( id );后通过createRoot( target )创建 React 19 风格的根节点写入偏好默认值通过dispatch( preferencesStore ).setDefaults(...)分别设置core/edit-post与core两个命名空间的默认偏好例如fullscreenMode: true默认全屏编辑、editorMode: visual、fixedToolbar: false、openPanels: [post-status]、showIconLabels: false等。这意味着文章编辑器的大量 UI 开关都收敛到了wordpress/preferences数据仓库中注册核心块调用registerCoreBlocks()、registerCoreBlockBindingsSources()并注册旧版小工具块registerLegacyWidgetBlock与组件组块registerWidgetGroupBlock。当globalThis.IS_GUTENBERG_PLUGIN为真时还会通过__experimentalRegisterExperimentalCoreBlocks注册实验性块环境兼容性检查检测document.compatMode若浏览器处于 Quirks Mode 则向控制台输出警告——该模式可能因 PHP 错误或!DOCTYPE html前存在 HTML 代码而触发导致块与 Meta Box 重叠渲染全局事件防御为dragover/drop添加preventDefault避免文件被拖拽到非投放区时浏览器默认打开文件预加载数据解析通过enablePreloadMultiUse()与preloadResolutions( postType, postId )驱动wordpress/core-data的 resolver 从预加载缓存中取数用户、实体配置、主题、全局样式、权限等待 Promise 完成后调用editorStore.setupEditor( post, initialEdits, settings.template )最终将Layout渲染进根节点返回 root函数末尾return root;调用方可继续控制该渲染根。上述实现印证了 README 对initialEdits的描述——它会在setupEditor时作为非用户发起的初始编辑传入从而跳过未保存更改提示。3.2 reinitializeEditor已废弃的空操作export function reinitializeEditor() { deprecated( wp.editPost.reinitializeEditor, { since: 6.2, version: 6.3, } ); }src/index.jsx 中该函数用于在出错后重新初始化编辑器但目前已是一个被标记废弃自 6.2 起弃用计划 6.3 移除的 noop——调用它只会触发wordpress/deprecated的废弃提示。因此新代码不应再依赖此 API。3.3 storecore/edit-post 数据仓库store是 edit-post 命名空间的数据仓库定义类型为Object。从 src/store/index.js 可见其由createReduxStore( STORE_NAME, { reducer, actions, selectors } )创建并立即register( store )注册STORE_NAME常量值为core/edit-post见 src/store/constants.js。也就是说你可以通过wp.data.select( core/edit-post )或wp.data.dispatch( core/edit-post )在控制台或插件代码中访问它。actions状态变更操作src/store/actions.js 中定义了一批 action值得注意的现状是绝大多数侧边栏/面板类 action 已标记废弃并委托给core/interface或core/editor例如openGeneralSidebar( name )/closeGeneralSidebar()打开/关闭通用侧边栏委托interfaceStore.enableComplementaryArea( core, name )openModal( name )/closeModal()自 6.3 起废弃改用core/interface的同名 actionopenPublishSidebar/closePublishSidebar/togglePublishSidebar自 6.6 起废弃改用core/editortoggleEditorPanelEnabled/toggleEditorPanelOpened/removeEditorPanel自 6.5 起废弃改用core/editorswitchEditorMode( mode )自 6.6 起废弃改用core/editor。仍保持活跃的 action 主要包括toggleFeature( feature )切换core/edit-post命名空间下的偏好开关写入 preferences storetogglePinnedPluginItem( pluginName )在工具栏上固定/取消固定插件项showBlockTypes( blockNames )/hideBlockTypes( blockNames )显示/隐藏指定块类型委托core/editor的私有 APItoggleFullscreenMode()切换全屏模式并弹出带 Undo 操作的 snackbar 通知Meta Box 相关initializeMetaBoxes()初始化postboxes脚本并挂接editor.savePost钩子以在保存时提交 Meta Box、requestMetaBoxUpdates()收集.metabox-base-form与各位置的 Meta Box 表单数据先触发window.tinyMCE.triggerSave()再 POST 到window._wpMetaBoxUrl、setAvailableMetaBoxesPerLocation、metaBoxUpdatesSuccess/Failure等。selectors状态读取src/store/selectors.js 提供状态查询能力同样存在大量废弃迁移活跃的getEditorMode读取core命名空间editorMode默认visual、isEditorSidebarOpened、isPluginSidebarOpened、getActiveGeneralSidebarName返回形如edit-post/document或my-plugin/insert-image-sidebar的活跃侧边栏名、getHiddenBlockTypes、isFeatureActive、isPluginItemPinned、getActiveMetaBoxLocations、isMetaBoxLocationVisible、hasMetaBoxes、isSavingMetaBoxes、getEditedPostTemplate等已废弃的getPreferences/getPreference自 6.0 起改用core/preferences、isPublishSidebarOpened6.6、isEditorPanelRemoved/Enabled/Opened6.5、isModalActive6.3、isInserterOpened/isListViewOpened6.5、isEditingTemplate6.5等。其中getPreferences的实现值得一提它为了兼容旧插件会把 preferences store 的inactivePanels/openPanels通过convertPanelsToOldFormat转换成旧的{ panelName: { enabled, opened } }结构——这从侧面说明本包承担了相当多的向后兼容职责。3.4 Plugin* 组件已迁移的插槽再导出README 的 API 列表中以下组件均以 Related: ... in wordpress/editor package 形式出现PluginBlockSettingsMenuItem块设置菜单项PluginDocumentSettingPanel文档侧边栏设置面板PluginMoreMenuItem更多菜单项PluginPostPublishPanel发布后面板PluginPostStatusInfo文章状态信息行PluginPrePublishPanel发布前面板PluginSidebar插件侧边栏PluginSidebarMoreMenuItem更多菜单中的侧边栏入口它们在 src/deprecated.jsx 中被定义为包装组件当 URL 路径包含site-editor.php即站点编辑器环境时直接返回null否则调用deprecateSlot( name )打印自 6.6 起废弃、请改用wp.editor.xxx的提示并把 props 透传给wordpress/editor中的同名组件。因此结论很明确面向未来开发的插件应直接从wordpress/editor全局wp.editor导入这些组件而不是从wp.editPost取用。README 将它们列入 API更多是历史兼容层面的记录。3.5 其他导出私有 API 与辅助组件src/index.jsx 末尾还导出了__experimentalFullscreenModeClose全屏模式的关闭按钮组件__experimentalMainDashboardButton主仪表盘按钮从wordpress/editor私有 API 解锁而来以及store与deprecated.jsx的全部导出。这些实验性/私有 API 通过 src/lock-unlock.js 中__dangerousOptInToUnstableAPIsOnlyForCoreModules的 lock/unlock 机制访问仅限 WordPress 核心模块使用插件与主题不应依赖其确认字符串亦明确声明private features are not for use in themes or plugins。四、扩展文章编辑器 UIregisterPlugin 与插件插槽README 明确指出扩展编辑器 UI 的唯一推荐入口是registerPluginAPI它让你把插件所有的 UI 元素定义在一个地方。该 API 由 packages/plugins/README.md 提供npm 包wordpress/pluginsWordPress 中对应脚本依赖wp-plugins全局wp.plugins。核心 API 一览API说明registerPlugin( name, settings )注册一个插件。name在所有已注册插件中必须唯一settings含icon、render、scope等getPlugin( name )返回已注册插件的设置WPPlugin \| undefinedgetPlugins( scope )返回无 scope 或指定 scope 的全部插件PluginArea渲染所有插件填充内容的组件默认隐藏 div支持scope与onErrorunregisterPlugin( name )注销插件usePluginContext()获取插件上下文推荐withPluginContext为 HOC已于 6.8.0 废弃例如通过PluginArea在自定义页面渲染带 scope 的插件import { PluginArea } from wordpress/plugins; const Layout () ( div Content of the page PluginArea scopemy-page / /div );在文章编辑器中PluginArea由核心在合适位置渲染插件只需用registerPlugin注册一个render函数render 内部返回任意插槽组件如PluginSidebar即可。五、实战从零搭建一个读写 Meta 字段的插件侧边栏下面这套完整流程取自仓库内的官方教程 docs/how-to-guides/plugin-sidebar-0.md它演示的正是 README Extending the post editor UI 章节所述能力用registerPluginPluginSidebar来自wp.editor扩展文章编辑器。Step 1让侧边栏跑起来创建plugin-sidebar.js( function ( wp, React ) { var el React.createElement; var registerPlugin wp.plugins.registerPlugin; var PluginSidebar wp.editor.PluginSidebar; registerPlugin( my-plugin-sidebar, { render: function () { return el( PluginSidebar, { name: my-plugin-sidebar, icon: admin-post, title: My plugin sidebar, }, Meta field ); }, } ); } )( window.wp, window.React );对应的 PHP 注册脚本注意脚本依赖必须包含wp-plugins、wp-editor、react并在enqueue_block_editor_assets钩子中入队?php /* Plugin Name: Sidebar plugin */ function sidebar_plugin_register() { wp_register_script( plugin-sidebar-js, plugins_url( plugin-sidebar.js, __FILE__ ), array( wp-plugins, wp-editor, react ) ); } add_action( init, sidebar_plugin_register ); function sidebar_plugin_script_enqueue() { wp_enqueue_script( plugin-sidebar-js ); } add_action( enqueue_block_editor_assets, sidebar_plugin_script_enqueue );激活后编辑器右上角会出现一个图钉样式的图标点击即可展开插件侧边栏。Step 2加入表单控件与样式使用wordpress/components中的TextControl创建输入框注意此时需把wp-components加入脚本依赖( function ( wp ) { var el React.createElement; var registerPlugin wp.plugins.registerPlugin; var PluginSidebar wp.editor.PluginSidebar; var TextControl wp.components.TextControl; registerPlugin( my-plugin-sidebar, { render: function () { return el( PluginSidebar, { name: my-plugin-sidebar, icon: admin-post, title: My plugin sidebar, }, el( div, { className: plugin-sidebar-content }, el( TextControl, { label: Meta Block Field, value: Initial value, onChange: function ( content ) { console.log( content changed to , content ); }, } ) ) ); }, } ); } )( window.wp );配套的plugin-sidebar.css给出内边距并在 PHP 中同时注册、入队样式.plugin-sidebar-content { padding: 16px; }Step 3注册 post_meta 字段字段需要暴露给 REST API块编辑器正是通过 REST 访问数据的register_post_meta( post, sidebar_plugin_meta_block_field, array( show_in_rest true, single true, type string, ) );验证字段是否已加载到编辑器 store浏览器控制台wp.data.select( core/editor ).getCurrentPost().meta;若返回undefined请确保你的 post type 支持custom-fields可通过register_post_type的supports参数或add_post_type_support启用。Step 4用 useSelect 初始化输入控件useSelect来自wordpress/data脚本依赖wp-data在组件加载时取数并在数据变化时更新。注意getEditedPostAttribute返回的是包含未保存编辑在内的最新值var MetaBlockField function () { var metaFieldValue useSelect( function ( select ) { return select( core/editor ).getEditedPostAttribute( meta )[ sidebar_plugin_meta_block_field ]; }, [] ); return el( Text, { label: Meta Block Field, value: metaFieldValue, onChange: function ( content ) { console.log( content has changed to , content ); }, } ); };可用下列命令验证组件随 store 更新输入框内容会变为 hello world!wp.data .dispatch( core/editor ) .editPost( { meta: { sidebar_plugin_meta_block_field: hello world! } } );Step 5用 useDispatch 写回 Meta 字段useDispatch( core/editor ).editPost会在每次按键时更新编辑器 storevar editPost useDispatch( core/editor ).editPost; return el( TextControl, { label: Meta Block Field, value: metaFieldValue, onChange: function ( content ) { editPost( { meta: { sidebar_plugin_meta_block_field: content }, } ); }, } );保存文章后重新加载编辑器输入控件会用数据库中最后保存的值重新初始化即证明数据已正确持久化到post_meta。避坑提示与自定义字段面板的冲突若用户在编辑器偏好右上角三点菜单 → Preferences → Panels中启用了Custom Fields编辑器底部会出现同名自定义字段输入框。该字段与侧边栏TextControl指向同一 meta 属性但保存时自定义字段的值会后写覆盖导致侧边栏中的改动丢失。两种解决方案将 meta 字段名加下划线前缀如_sidebar_plugin_meta_block_field将其标记为私有 meta从而不显示在 Custom Fields 中——此时需在register_post_meta的args中提供最终返回true的auth_callback在TextControl的onChange中同步更新 Custom Fields 对应 textarea 的值保证两处一致return el( TextControl, { label: Meta Block Field, value: metaFieldValue, onChange: function ( content ) { editPost( { meta: { sidebar_plugin_meta_block_field: content } }) document.querySelector( {the-value-textarea} ).innerHTML content; }, } );若不需要启用 Custom Fields则不存在此问题。六、八种插槽组件速查以下组件的完整文档在 packages/editor/README.md约 541–977 行它们用于将插件 UI 注入文章编辑器不同区域。本包在历史版本中曾再导出这些组件现位于 src/deprecated.jsx因此理解它们等于理解 edit-post 的扩展点。PluginSidebar在编辑器最右侧渲染一个可固定pinnable的侧边栏当isPinnable为true时自动渲染对应的菜单项。namestring必填侧边栏标识插件作用域内必须唯一titlestring侧边栏顶部标题iconWPBlockTypeIconRenderDashicon 名称或 SVG 元素用于固定到工具栏时显示isPinnableboolean是否允许固定到工具栏classNamestring附加到侧边栏主体的类名。import { __ } from wordpress/i18n; import { PanelBody } from wordpress/components; import { PluginSidebar } from wordpress/editor; import { more } from wordpress/icons; const MyPluginSidebar () ( PluginSidebar namemy-sidebar titleMy sidebar title icon{ more } PanelBody{ __( My sidebar content ) }/PanelBody /PluginSidebar );手动打开侧边栏无需PluginSidebarMoreMenuItem也可以用 dispatch APIwp.data .dispatch( core/edit-post ) .openGeneralSidebar( plugin-name/sidebar-name );注意上述openGeneralSidebar正是 src/store/actions.js 中仍活跃的 action 之一它通过interfaceStore.enableComplementaryArea( core, name )实现。PluginSidebarMoreMenuItem在更多菜单More Menu的 Plugins 分组中渲染一个菜单项用于激活对应的PluginSidebar。targetstring必填目标侧边栏的name必须与PluginSidebar的name一致iconWPBlockTypeIconRender显示在菜单项文本左侧的图标。PluginDocumentSettingPanel在文档侧边栏的状态与可见性Status Visibility面板下方渲染一个设置面板。namestring必填面板的机器友好名称titlestring面板标题className、icon、children类名、固定图标与子内容。const MyDocumentSettingPlugin () ( PluginDocumentSettingPanel classNamemy-document-setting-plugin titleMy Panel namemy-panel { __( My Document Setting Panel ) } /PluginDocumentSettingPanel ); registerPlugin( my-document-setting-plugin, { render: MyDocumentSettingPlugin } );PluginPostStatusInfo在文档侧边栏的 Summary 面板中渲染一行信息该组件围绕功能职责而非位置命名位置未来可能变化。classNamestring附加到该行的类名children要渲染的内容。PluginPrePublishPanel / PluginPostPublishPanel分别在发布流程的发布前点击发布按钮后弹出的面板与发布后发布成功后面板渲染内容。titlestring面板顶部标题initialOpenboolean是否默认展开未提供标题时总是展开icon固定到工具栏时的图标传false则不渲染图标className、children类名与内容。PluginBlockSettingsMenuItem在选中块时的块设置菜单中渲染一个新菜单项。labelstring菜单项文本onClickFunction点击回调iconDashicon 名称或 SVG 元素allowedBlocksArray限定仅在哪些块如[core/paragraph]上显示未提供则对所有块显示多选时仅当全部选中块都在列表中才显示smallboolean是否隐藏 label 仅显示图标rolestring菜单项的 ARIA role。PluginMoreMenuItem在更多菜单的 Plugins 分组中渲染一个菜单项可作按钮或链接使用组件内文本即菜单项标签。icon菜单项图标onClick点击回调。七、迁移与兼容性从 wp.editPost 走向 wp.editor结合 src/deprecated.jsx 与 src/store/actions.js、src/store/selectors.js 中的大量deprecated()调用可以清晰看到本包近几个版本的核心演进方向扩展能力整体向wordpress/editor与wordpress/interface收敛。旧用法core/edit-post/wp.editPost新用法废弃起始版本PluginSidebar等 8 个组件wordpress/editor中的同名组件wp.editor.*6.6openModal/closeModalcore/interface6.3getPreferences/getPreferencecore/preferences6.0openPublishSidebar等core/editor6.6toggleEditorPanel*/removeEditorPanelcore/editor6.5switchEditorModecore/editor6.6isInserterOpened/isListViewOpenedcore/editor6.5reinitializeEditor无noop6.2 → 计划 6.3 移除对存量插件而言这些deprecated包装仍然可用会打印控制台提示对新插件而言直接使用wp.editor与wp.plugins是更稳妥的选择。另外需要留意PluginSidebar等组件在站点编辑器site editor环境下会直接返回null见 src/deprecated.jsx 中基于site-editor.php的判断插件若需兼容站点编辑器应采用wordpress/editor包中针对双环境设计的对应方案。八、延伸阅读与仓库导航本包主体文档packages/edit-post/README.md初始化与导出实现packages/edit-post/src/index.jsx插槽组件废弃包装packages/edit-post/src/deprecated.jsxStore 定义与常量packages/edit-post/src/store/index.js、packages/edit-post/src/store/constants.jsStore 的 actions 与 selectorspackages/edit-post/src/store/actions.js、packages/edit-post/src/store/selectors.js私有 API 锁机制packages/edit-post/src/lock-unlock.jsURL 与文章 ID 同步组件说明packages/edit-post/src/components/browser-url/README.md插件注册 APIpackages/plugins/README.md扩展组件的完整参数文档packages/editor/README.md侧边栏完整教程docs/how-to-guides/plugin-sidebar-0.md数据访问useSelect/useDispatchpackages/data/README.mdES2015 polyfillwordpress/babel-preset-default结语wordpress/edit-post是理解 Gutenberg 文章编辑器如何被组装起来的最佳入口从initializeEditor的初始化管线、core/edit-poststore 的偏好与 Meta Box 管理到经由registerPlugin接入的八大插槽组件它把 WordPress 文章编辑场景的方方面面串成一条完整的扩展链路。对于插件开发者而言掌握本文的侧边栏实战流程与迁移指南即可在文章编辑器中稳定地构建自定义 UI 并读写post_meta同时确保代码与 WordPress 核心的演进方向保持一致。【免费下载链接】gutenbergThe Block Editor project for WordPress and beyond. Plugin is available from the official repository.项目地址: https://gitcode.com/GitHub_Trending/gu/gutenberg创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考