尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Gutenberg 导航块共享组件指南:Navigation Link 与 Navigation Submenu 的统一控制层实现

Gutenberg 导航块共享组件指南:Navigation Link 与 Navigation Submenu 的统一控制层实现 Gutenberg 导航块共享组件指南Navigation Link 与 Navigation Submenu 的统一控制层实现【免费下载链接】gutenbergThe Block Editor project for WordPress and beyond. Plugin is available from the official repository.项目地址: https://gitcode.com/GitHub_Trending/gu/gutenberg导读本文深入解析 Gutenberg 插件仓库中packages/block-library/src/navigation-link/shared/目录的设计理念与实现细节。该目录承载了 Navigation Link导航链接与 Navigation Submenu导航子菜单两个块共用的侧边栏检查器Inspector Controls组件与一系列链接状态管理 Hook用于消除重复代码、保证两个块行为一致。读完本文你将掌握共享组件目录的完整目录结构、Controls组件的每个设置项及其属性映射、实体绑定Entity Binding与失效链接校验的底层逻辑以及如何为这两个导航块扩展新的共享功能。为什么需要共享目录两个导航块的功能重叠Navigation Link 和 Navigation Submenu 在编辑器中共享大量行为尤其是侧边栏检查器中的 ToolsPanel 界面。在引入共享目录之前两者各自维护一份几乎相同的检查器代码带来三个问题修改功能需要在两处同步改动、细微的行为漂移难以察觉、重复代码放大了出错的面积。共享目录的建立正是为了解决这些痛点见 README减少代码重复消除两个块之间完全相同的代码保证一致性两个块使用同一组件杜绝行为差异降低维护负担共享功能的改动只在一处进行最小化缺陷重复代码越少隐藏 bug 的位置越少提升可测试性共享组件只需测试一次即可被两个块复用。共享目录结构与导出清单当前共享目录位于 packages/block-library/src/navigation-link/shared/其核心文件如下shared/ ├── README.md # 设计说明 ├── index.js # 共享模块统一出口 ├── controls.jsx # 核心共享组件 ControlsToolsPanel 检查器 ├── update-attributes.js # 链接属性计算与实体链接判定 ├── use-handle-link-change.js # 链接选择/变更处理 Hook ├── use-entity-binding.js # 实体绑定创建/清理 Hook ├── use-link-preview.js # 链接预览数据计算 Hook ├── use-is-invalid-link.js # 失效/草稿链接检测 Hook ├── use-enable-link-status-validation.js # 校验开关 Hook ├── use-is-dragging-within.js # 拖拽检测 Hook ├── select-label-text.js # 标签文本选择工具 ├── invalid-draft-display.jsx # 失效/草稿提示组件 ├── item-should-render.php # PHP 端渲染判断 ├── render-submenu-icon.php # 子菜单图标渲染 └── test/ # 全部共享组件的测试所有共享能力通过 index.js 统一导出包括Controls、getInvalidLinkHelpText、updateAttributes、useEntityBinding、buildNavigationLinkEntityBinding、LinkUI、useHandleLinkChange、useIsInvalidLink、InvalidDraftDisplay、useEnableLinkStatusValidation、useIsDraggingWithin、selectLabelText、useLinkPreview。两个导航块均从这个出口导入例如 Navigation Submenu 的编辑文件在 edit.jsx 中通过import { Controls, ... } from ../navigation-link/shared消费共享能力。Controls 组件两个块共用的检查器面板Controls定义于 controls.jsx是共享目录的核心组件接收attributes、setAttributes、clientId和isLinkEditable四个 props渲染一个以__experimentalToolsPanel为基础的 Settings 面板。它在两个块中的调用方式略有差异Navigation Link 直接渲染全部控件而 Navigation Submenu 在 edit.jsx 中通过isLinkEditable{ ! openSubmenusOnClick }控制链接编辑区域是否可用——当子菜单配置为点击打开时禁止直接编辑链接。面板内的设置项与属性映射面板包含五个ToolsPanelItem每个都实现了hasValue/onDeselect语义用于重置全部和单项移除设置项控件类型绑定的块属性默认显示说明TextTextControllabel是链接文本展示前经__unstableStripHTML去除 HTMLLink toLinkPickerurl/id/kind/type是由isLinkEditable门控的链接选择器Open in new tabCheckboxControlopensInNewTab是为真时链接target_blankDescriptionTextareaControldescription非 contentOnly 模式主题支持时显示在菜单中Rel attributeTextControlrel非 contentOnly 模式空格分隔的链接类型关系值重置全部按钮会将五个属性统一还原label: 、url: 、description: 、rel: 、opensInNewTab: false。条件渲染与内容编辑模式组件内部通过useSelect读取blockEditingMode当编辑模式为contentOnly仅内容编辑时Description 与 Rel attribute 两项默认隐藏isShownByDefault{ ! isContentOnly }保证内容编辑界面聚焦于文本与链接本身。辅助操作按钮当链接绑定到存在的文章/页面实体时hasUrlBinding、isBoundEntityAvailable且kind post-type面板会渲染Edit按钮回调编辑器的onNavigateToEntityRecord跳转到对应文章编辑页当 URL 可查看时非纯 hash 链接或站点内相对路径渲染带external图标的View按钮在新标签页打开链接站点内以/开头的绝对路径会拼接站点首页homeUrl构造完整地址。面向用户的帮助文案getInvalidLinkHelpText()返回该链接无效不会显示在你的站点上请更新链接的错误提示getDraftHelpText()则根据实体类型post/page/category/tag 等由getEntityTypeName映射为可读名称生成链接指向草稿的提示告诉用户草稿发布前链接不会出现在站点上。链接有效性校验isInvalid 与 isDraft 的判定逻辑use-is-invalid-link.js 负责判定导航链接是否失效或指向草稿仅当kind post-type或 type 为 post/page且 id 为整数时才执行校验通过coreStore.getEntityRecord( postType, type, id )获取文章记录并配合hasFinishedResolution判断实体是否已被删除当实体不存在已删除或status trash时判定为失效isInvalid当status draft时判定为草稿isDraft在blockEditingMode disabled或校验未启用时直接跳过——源码注释明确指出获取文章状态对大型导航站点而言是昂贵操作模板等禁用上下文里没有价值因此跳过以节省请求。Controls组件据此显示相应帮助文案同时InvalidDraftDisplay组件invalid-draft-display.jsx负责在画布中提示失效/草稿状态。实体绑定让链接跟随文章更新use-entity-binding.js 实现导航链接与内容实体文章、页面、分类、标签之间的 Block Bindings 绑定使 URL 能随实体数据自动更新。绑定配置的生成buildNavigationLinkEntityBinding( kind )是绑定配置的工厂函数只接受post-type与taxonomy两种 kind传入其他值会抛出明确错误。生成的绑定配置将url字段绑定到对应数据源kind post-type绑定到core/post-data数据源字段为linkkind taxonomy绑定到core/term-data数据源字段为link。绑定生命周期管理Hook 返回hasUrlBinding判断metadata.bindings.url是否存在且数据源正确、isBoundEntityAvailable通过 core-data 解析实体记录解析完成后记录不存在即判定实体已删除、entityRecord、createBinding与clearBinding。创建与清理均通过useBlockBindingsUtils的updateBlockBindings完成且createBinding内部捕获buildNavigationLinkEntityBinding的校验异常并输出console.warn保证无效 kind 不会产生脏绑定。链接变更的完整流程use-handle-link-change.js 把用户从 LinkPicker 选中一个链接到属性与绑定落盘的完整链路封装为单一回调从updatedLink提取url、kind、type、id计算文本更新仅当allowTextUpdate为真且新标题与当前label不同才写入label写入前经escapeHTML转义检测实体链接 → 自定义链接的转换! updatedLink.id hasUrlBinding先clearBinding()再直接通过 block-editor store 的updateBlockAttributes写入kind: custom、type: custom、id: undefined——源码注释特别说明直接走 store 分发是为了绕过setBoundAttributes对已绑定属性的更新拦截常规流程调用updateAttributes计算最终属性然后依据最终状态决定createBinding实体链接或clearBinding自定义链接。属性计算引擎 updateAttributes实体链接如何切断update-attributes.js 是链接属性计算的核心工具函数接收新值、setAttributes与当前属性负责标签回退新标签优先取title其次取label若没有可用标题则回退为去除http(s)://前缀的 URL类型规范化post_tag统一存为tag其余类型中的-替换为_源码注释引用了 PR #24670 中的决策自定义链接判定非内置类型post/page/tag/category且无 kind或 kind 为custom时归类为自定义链接URL 防双重编码先safeDecodeURI再encodeURI重新编码避免传入的 URL 已被编码导致双重编码实体链接切断sever当块已存在id即链接指向实体但新 URL 未携带新 id 时通过shouldSeverEntityLink比较新旧 URL 的 hostname 与 path去除尾斜杠规范化并特判?p与?page_id两种纯固定链接参数只要主机名或路径变化即判定应切断将id显式置为undefined并把 kind/type 改为custom使链接退化为普通自定义链接。函数返回{ isEntityLink, attributes }其中isEntityLink的判定使用id in attributes运算符区分属性未设置与显式置为 undefined这是切断逻辑正确性的关键。链接预览useLinkPreview 与徽章体系use-link-preview.js 为 LinkPicker 计算展示数据包含标题、展示 URL、缩略图与徽章标题优先取实体记录的渲染标题文章类实体的title.rendered无标题显示 (no title)无标题时通过 block-editor 私有 API 的useRemoteUrlData抓取远程页面标题最终回退到解码后的 URL展示 URLcomputeDisplayUrl区分站内/站外——站内链接只展示 pathname可带查询参数与锚点跨主机判定为外部链接hash 链接与相对路径恒为站内缩略图仅对具有featured_media的文章类实体通过 attachment 记录取 thumbnail/medium 尺寸图源徽章badgescomputeBadges按优先级生成徽章包括 External link外部、Internal linkhash 链接、Homepage通过isHomepage比较主机名与规范化路径、实体类型徽章page/post/category…、以及状态徽章——Publishedsuccess、Scheduled/Draft/Pendingwarning、Privatedefault、Trash与Missing %serror、No link selectederror。校验时机优化useEnableLinkStatusValidationuse-enable-link-status-validation.js 决定链接状态校验是否开启仅当根 Navigation 块自身被选中、或其任意内部块被选中getSelectedBlockClientId与hasSelectedInnerBlock( rootNavigationId, true )时才返回true确保昂贵的实体状态查询只在用户活跃编辑导航结构时发生。测试策略纯 Mock 隔离README 强调所有共享组件在test/目录配有综合测试采用纯 Mock 策略保证测试的隔离性与可靠性。以 controls.jsdom.test.jsx 为例通过vi.mock将updateAttributes、useToolsPanelDropdownMenuProps、useEntityBinding、useIsInvalidLink等外部依赖全部替换为可控的 mock 实现使组件测试只关注自身渲染与交互测试覆盖渲染全部五个表单控件、从 label 值剥离 HTMLstrongBold Text/strong输入显示为Bold Text、以及各字段变更时setAttributes被正确调用如 rel 值nofollow noopener原样写入。同目录下的 update-attributes.test.js 等文件分别覆盖属性计算与各 Hook 的行为整个共享目录实现组件测试一次两个块复用的可测试性目标。未来方向迈向统一的 Navigation Item 块README 指出共享目录解决的是当下的重复代码问题长期愿景是重构为统一的 Navigation Item 块——同一块根据上下文链接或子菜单表现不同行为。这将消除独立的 Navigation Link 与 Navigation Submenu 两个块、提供单一更易维护的代码库、支持更灵活的导航项类型并简化用户体验。但该重构超出当前范围、需要重大架构变更共享目录作为过渡方案在保持向后兼容、降低技术债的同时为未来的统一打下了基础并持续支撑 Dynamic URL 等新功能的集成。贡献指南如何新增共享功能按照 README 的贡献流程新增共享能力时应遵循以下步骤将共享组件放入本目录packages/block-library/src/navigation-link/shared/从 index.js 导出为组件补充综合测试沿用纯 Mock 策略让 Navigation Link 与 Navigation Submenu 两个块改用共享组件删除两个块各自的重复代码。小结navigation-link/shared/是 Gutenberg 导航体系去重与统一的样板实践以Controls为界面的统一入口以useEntityBinding、useHandleLinkChange、updateAttributes、useIsInvalidLink、useLinkPreview等 Hook 与工具函数组成完整的链接数据链路并在测试层面用纯 Mock 策略保障质量。对于想要理解 Gutenberg 块级代码复用模式、或准备为导航块贡献新能力的开发者这个目录是理想的起点。【免费下载链接】gutenbergThe Block Editor project for WordPress and beyond. Plugin is available from the official repository.项目地址: https://gitcode.com/GitHub_Trending/gu/gutenberg创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表