news 2026/9/20 9:06:35

Gutenberg Interactivity API 客户端导航实战:interactivity-router 的区域路由、预取与源码级实现解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Gutenberg Interactivity API 客户端导航实战:interactivity-router 的区域路由、预取与源码级实现解析

Gutenberg Interactivity API 客户端导航实战:interactivity-router 的区域路由、预取与源码级实现解析

【免费下载链接】gutenbergThe Block Editor project for WordPress and beyond. Plugin is available from the official repository.项目地址: https://gitcode.com/GitHub_Trending/gu/gutenberg

本篇基于 Gutenberg 仓库的官方参考文档client-side-navigation.md,系统讲解 WordPress Interactivity API 提供的客户端导航(Client-Side Navigation)机制:如何声明 router region、接入@wordpress/interactivity-router、实现navigate()/prefetch()调用,并深入解析路由器的内存页缓存、CSS 级联顺序维护(SCS 算法)、脚本模块预载与服务端状态合并等内部实现,帮助你在区块或传统主题中构建无需整页刷新的应用级页面切换体验。

客户端导航是什么,以及两种模式

客户端导航是一种无需整页重新加载即可在页面之间跳转的技术:浏览器不再从服务器获取全新的 HTML 文档,而是只获取目标页面的内容并更新 DOM 中发生变化的部分,从而带来更快、更平滑的页面过渡和更接近原生应用的体验。

Interactivity API 通过@wordpress/interactivity-router包提供这一能力,其核心概念是router region(路由区域):页面上路由器知道如何在导航时更新的一块区域。你用data-wp-router-region指令标记这些区域;当用户导航到新 URL 时,路由器获取目标页面并只替换匹配区域的内容——页面其余部分保持不动。

Interactivity API 支持两种导航模式:

  • 基于区域的客户端导航——在 WordPress 中实现客户端导航的推荐方式;
  • 整页客户端导航(实验性)——将整个<body>元素视为单一区域,实质上在不进行传统刷新的情况下更新整页内容(见文末整页客户端导航(实验性))。

客户端导航的工作原理

当用户触发一次导航(例如点击带有data-wp-on--click指令且该指令调用actions.navigate()的链接)时,Interactivity Router 会依次执行:

  1. 获取新页面:路由器请求目标 URL 的 HTML。
  2. 解析响应:从获取到的 HTML 中提取相关的区域、样式、脚本模块和服务端渲染的数据。
  3. 更新 DOM:只有指定"路由区域"内的内容被替换。
  4. 更新浏览器历史:向浏览器会话历史添加新条目(或在指定时替换当前条目)。
  5. 加载必要资源:在新页面渲染之前,加载其所需的新样式或脚本模块。
  6. 处理可访问性:通过屏幕阅读器播报来提示导航进度。

这一方案带来若干收益:性能更好(只更新变化部分,减少数据传输与 DOM 操作)、状态保留(全局状态与本地上下文在导航间持久存在)、过渡平滑(无白屏闪烁)、SEO 友好(服务器仍然渲染完整的 HTML 页面,搜索引擎可正常抓取)。

在源码中,这一整套流程由 packages/interactivity-router/src/index.ts 中core/routerstore 的navigateaction 驱动:它先做 URL 归一化与页缓存查询,再Promise.race等待页面就绪或超时,最后用batch()批量完成区域渲染、URL 更新与历史栈写入。

开始使用 Interactivity Router

@wordpress/interactivity-router包自 WordPress 6.5 起随核心分发。新建项目时,最简单的方式是使用仓库中的@wordpress/create-block-interactive-template脚手架工具,它提供专用的client-side-navigation变体,可一键生成已完整接好客户端导航的区块——包含 router region、prev/next 导航、加载指示器,以及一个跨导航持久化运行的秒表来演示状态保持:

npx @wordpress/create-block@latest my-interactive-block --template @wordpress/create-block-interactive-template --variant client-side-navigation

你也可以先脚手架化默认变体,再自行添加客户端导航:

npx @wordpress/create-block@latest my-interactive-block --template @wordpress/create-block-interactive-template

无论做区块还是传统主题,添加客户端导航都遵循同样四步:

  1. 添加路由器依赖:把@wordpress/interactivity-router作为脚本模块的动态依赖。
  2. 确保脚本模块在导航时加载:标记脚本模块,让路由器知道新页面需要加载它。
  3. 定义路由区域:用data-wp-router-region属性标记导航时需要更新的 HTML 元素。
  4. 触发导航:使用路由器的actions.navigate()函数进行编程式导航。

其中步骤 1、2 因区块/传统主题而异,步骤 3、4 完全相同。

添加路由器依赖

@wordpress/interactivity-router模块应作为动态依赖添加,使其仅在需要时才被获取。

区块场景下,在view.js中动态import该包即可,区块构建工具(wp-scripts)会检测动态 import 并自动注册 PHP 侧依赖:

const { actions } = yield import( '@wordpress/interactivity-router' ); yield actions.navigate( url );

传统主题场景下,不依赖区块的block.json,而是手动注册并加载脚本模块,把@wordpress/interactivity-router列为动态依赖;同时把 Interactivity API 指令直接写入主题模板文件,并用wp_interactivity_process_directives()处理(详见服务端渲染文档):

// functions.php add_action( 'wp_enqueue_scripts', function () { wp_register_script_module( 'my-theme/navigation', get_template_directory_uri() . '/assets/navigation.js', array( '@wordpress/interactivity', array( 'id' => '@wordpress/interactivity-router', 'import' => 'dynamic', ), ) ); wp_enqueue_script_module( 'my-theme/navigation' ); } );

确保脚本模块在导航时加载

客户端导航过程中,路由器需要知道新页面应加载哪些脚本模块。它通过在<script>标签上查找data-wp-router-options属性(且其中loadOnClientNavigationtrue)来识别。缺少该属性,路由器就不会在客户端导航时加载此脚本模块,区块的交互性在新页面上将失效。这一点在源码 packages/interactivity-router/src/assets/script-modules.ts 中可以直接验证:预载阶段使用选择器script[type=module][src][data-wp-router-options]扫描文档,再解析属性 JSON 判断loadOnClientNavigation;配套测试 packages/interactivity-router/src/assets/test/script-modules.jsdom.test.ts 明确断言"只预载loadOnClientNavigation: true的模块",且对非法 JSON 优雅降级。

区块场景下,只要block.json声明了 interactivity 支持,该属性就会自动添加。以下两种配置均有效:

{ "supports": { "interactivity": true } }
{ "supports": { "interactivity": { "clientNavigation": true } } }

如果block.json已包含其中之一,则无需额外配置——WordPress 会处理其余工作。

传统 PHP 主题以及其他在block.json之外注册的脚本模块,属性不会自动添加。你必须显式使用add_client_navigation_support_to_script_module()为其注册客户端导航支持:

wp_interactivity()->add_client_navigation_support_to_script_module( 'my-theme/navigation' );

缺少这一步,路由器导航到需要该模块的页面时就不会加载它。

设置路由区域

路由区域是导航时路由器会更新的页面区域。你通过在同一个元素上同时添加data-wp-router-regiondata-wp-interactive来定义它——目前两者缺一不可。

data-wp-router-region指令的值是一个唯一 ID。导航发生时,路由器通过 ID 将当前页面的区域与目标页面的区域进行匹配并替换其内容——区域之外的所有东西都不受影响。每个区域 ID 在一页内必须唯一;若两个区域共用同一 ID,路由器将无法判断该更新哪一个。源码中 packages/interactivity-router/src/index.ts 定义了扫描用的属性常量与选择器,且 parseRegionAttribute 会先尝试JSON.parse,失败时回退为纯字符串 ID——这正是下面两种书写方式能并存的原因。

一个基础的路由区域:

<div ><div ><div><div><a>// view.js import { store, withSyncEvent } from '@wordpress/interactivity'; store( 'myPlugin', { actions: { navigateTo: withSyncEvent( function* ( event ) { event.preventDefault(); const { actions } = yield import( '@wordpress/interactivity-router' ); yield actions.navigate( event.target.href ); } ), }, } );

withSyncEvent()包装器是必须使用的:凡是需要同步调用事件方法(如event.preventDefault())的 action 都要用它,详见 withSyncEvent() 文档。

实现预取

路由器还提供prefetch()函数:它获取页面并放入内部内存缓存,但不执行导航。在用户点击之前预取页面,可以让随后的导航"零延迟"——因为内容已经就绪。

常见模式是鼠标悬停时预取、点击时导航。可以用两条指令把两种行为组合在同一元素上——data-wp-on--mouseenter负责预取、data-wp-on--click负责导航:

<a >// view.js import { store, withSyncEvent } from '@wordpress/interactivity'; store( 'myPlugin', { actions: { prefetchPage: function* ( event ) { const { actions } = yield import( '@wordpress/interactivity-router' ); yield actions.prefetch( event.target.href ); }, navigateTo: withSyncEvent( function* ( event ) { event.preventDefault(); const { actions } = yield import( '@wordpress/interactivity-router' ); yield actions.navigate( event.target.href ); } ), }, } );

完整示例:分页

本例把路由区域、导航与预取组合起来,为一组文章列表实现客户端分页。PHP 模板查询当前页的文章并渲染进路由区域,底部是"上一页/下一页"链接:悬停时预取目标页,点击时路由器执行客户端导航——只替换路由区域内的内容,不做整页刷新;导航完成后页面平滑滚动到顶部。

PHP:

<?php $current_page = isset( $_GET['paged'] ) ? absint( $_GET['paged'] ) : 1; $query = new WP_Query( array( 'paged' => $current_page, 'posts_per_page' => 5, ) ); ?> <div >import { store, withSyncEvent } from '@wordpress/interactivity'; store( 'myPagination', { actions: { prefetch: function* ( event ) { const { actions } = yield import( '@wordpress/interactivity-router' ); yield actions.prefetch( event.target.href ); }, navigate: withSyncEvent( function* ( event ) { event.preventDefault(); const { actions } = yield import( '@wordpress/interactivity-router' ); yield actions.navigate( event.target.href ); // 导航后滚动到顶部。 window.scrollTo( { top: 0, behavior: 'smooth' } ); } ), }, } );

进阶用例

处理滚动与焦点

路由器不会在导航后自动管理滚动位置或焦点——这是调用actions.navigate()的 action 的责任。客户端导航完成后,页面会停留在当前滚动位置,焦点仍留在触发导航的元素上(若该元素在区域更新中被移除,则焦点丢失)。应在导航 action 中显式处理,例如导航后滚动到顶部:

store( 'myPlugin', { actions: { navigateTo: withSyncEvent( function* ( event ) { event.preventDefault(); const { actions } = yield import( '@wordpress/interactivity-router' ); yield actions.navigate( event.target.href ); // 导航后滚动到顶部。 window.scrollTo( { top: 0, behavior: 'smooth' } ); } ), }, } );

出于可访问性考虑,导航后应将焦点移到有意义的元素上(如主内容区或标题),让键盘与屏幕阅读器用户知道自己在新一页的哪个位置。

attachTo在导航时动态添加区域

有时你需要某些 UI 元素——如模态框、侧边栏、通知面板——只出现在特定页面上。普通路由区域要求区域在当前页面已存在才能被更新;attachTo选项解决了这个问题:它可以定义"当导航到存在该区域的页面时动态创建并插入 DOM 的区域",即使原页面上没有它。

attachTo的区域定义:

<div ><div ><div ><div ><div ><!-- 好:稳定、源自数据的 key --> <li>import { store, getContext, getServerState, getServerContext, } from '@wordpress/interactivity'; const { state } = store( 'myPlugin', { callbacks: { syncWithServer() { const serverState = getServerState(); const serverContext = getServerContext(); const context = getContext(); // 让商品计数在导航间与服务器保持同步。 if ( serverState.productCount !== undefined ) { state.productCount = serverState.productCount; } // 根据新页面的上下文重置展开状态。 if ( serverContext.isExpanded !== undefined ) { context.isExpanded = serverContext.isExpanded; } }, }, } );

更多细节可参考理解全局状态、本地上下文与派生状态指南。

覆盖路由器内部内存缓存中的页面

默认情况下,一旦页面进入路由器的内部内存缓存,后续导航将直接使用缓存版本,不再发起新的网络请求。使用force选项可以绕过缓存、从服务器重新获取页面:

// 用 navigate() 强制重新获取。 yield actions.navigate( '/products/', { force: true } ); // 用 prefetch() 强制重新获取。 yield actions.prefetch( '/products/', { force: true } );

警告:若在变更后(POST/PUT/DELETE 请求)用force: true刷新页面,务必确保变更已完成再导航:

store( 'myPlugin', { actions: { deleteAndRefresh: function* () { // 等待删除完成。 yield fetch( '/wp-json/wp/v2/posts/123', { method: 'DELETE' } ); // 现在刷新页面以展示更新后的数据。 const { actions } = yield import( '@wordpress/interactivity-router' ); yield actions.navigate( window.location.href, { force: true } ); }, }, } );

对应源码逻辑见 packages/interactivity-router/src/index.ts 的prefetchif ( options.force || ! pages.has( pagePath ) )——只有force为真或缓存未命中时才会重新写入并发起fetchPage

使用自定义 HTML

不必从 URL 获取页面,也可以用html选项直接提供 HTML:

// 带自定义 HTML 导航。 yield actions.navigate( '/custom-page/', { html: ` <div>// 默认行为:添加新历史条目(pushState)。 yield actions.navigate( '/page-2/' ); // 替换当前历史条目(replaceState)。 yield actions.navigate( '/page-2/', { replace: true } );

以下情况适合使用replace: true:为过滤/排序更新查询参数(每次变化都不应成为独立历史条目);实现无限滚动(更新 URL 但不希望每页都是独立历史条目)。源码中可见 index.ts 正是用options.replace ? 'replaceState' : 'pushState'完成的。

修改超时时间

若导航耗时过长,路由器会回退到传统的整页加载。默认超时为 10 秒(源码中 navigate 的解构默认值timeout = 10000)。用timeout选项修改:

// 更短的超时以更快失败。 yield actions.navigate( '/page/', { timeout: 5000 } ); // 慢速连接使用更长的超时。 yield actions.navigate( '/page/', { timeout: 30000 } );

处理 fetch 错误

当导航失败(网络错误、超时或服务端错误)时,路由器会自动回退到整页重新加载。这意味着你无法直接从navigate()捕获 fetch 错误——浏览器会抢在你的代码处理之前接管。

若需要自定义错误处理(例如显示错误信息而不是重新加载),可以手动 fetch 页面、自行处理错误,再把获取到的 HTML 通过html选项传给navigate()

store( 'myPlugin', { actions: { navigateWithCustomErrorHandling: withSyncEvent( function* ( event ) { event.preventDefault(); const url = event.target.href; try { // 手动获取页面。 const response = yield fetch( url ); if ( ! response.ok ) { // 处理 HTTP 错误。 state.error = `错误:${ response.status }`; return; } const html = yield response.text(); // 使用获取到的 HTML 执行导航。 const { actions } = yield import( '@wordpress/interactivity-router' ); yield actions.navigate( url, { html } ); } catch ( error ) { state.error = '网络错误,请检查你的连接。'; } } ), }, } );

在某些页面禁用客户端导航

某些页面可能必须整页重新加载。使用wp_interactivity_config()禁用客户端导航:

// 在主题的 functions.php 或插件中。 add_action( 'wp', function() { // 在特定页面模板上禁用。 if ( is_page_template( 'template-complex.php' ) ) { wp_interactivity_config( 'core/router', array( 'clientNavigationDisabled' => true ) ); } } );

clientNavigationDisabledtrue时:actions.navigate()触发整页重新加载;actions.prefetch()不做任何事;从其他页面导航到该页面时强制重新加载。这与源码行为一致:navigate 首行 与 prefetch 首行 均读取getConfig()中的clientNavigationDisabled,命中后分别forcePageReload( href )或直接 return;此外,目标页自身的 config 也会检查该标记,防止导航"进入"一个要求整页加载的页面。

禁用导航反馈

Interactivity API 路由器内置了导航期间的反馈:加载动画——页面顶部的进度条,在导航开始后 400ms 仍未完成时出现(源码中 index.ts 用一个 400ms 的setTimeout才把navigation.hasStarted置真——这 400ms 延迟正是为了避免页面已被预取或连接极快时出现闪烁的动画);屏幕阅读器播报——提示导航进度的可访问性播报(通过a11ySpeak()动态引入@wordpress/a11yspeak完成)。

有时你希望禁用它们:

// 禁用加载动画(用于"瞬间完成"感的更新)。 yield actions.navigate( '/page/', { loadingAnimation: false } ); // 禁用屏幕阅读器播报(当你提供自定义播报时)。 yield actions.navigate( '/page/', { screenReaderAnnouncement: false } ); // 同时禁用两者。 yield actions.navigate( '/page/', { loadingAnimation: false, screenReaderAnnouncement: false, } );

禁用反馈的典型场景:静默更新(不想引起注意的后台刷新);自定义加载 UI(实现自己的加载指示器);自定义可访问性(提供自己的屏幕阅读器播报)。

订阅页面变化

core/routerstore 暴露了一个响应式的state.url属性,每次客户端导航发生时都会更新。在data-wp-watchwatch回调中读取它,即可建立一个"URL 一变就重跑"的响应式订阅:

// view.js import { watch, store } from '@wordpress/interactivity'; // Store 级别的订阅。 watch( () => { const { state } = store( 'core/router' ); sendAnalyticsPageView( state.url ); } ); // 基于元素的订阅:<div>// 表单提交后强制重新获取。 const { actions } = store( 'myPlugin', { actions: { *submitForm() { yield fetch( '/wp-json/my-plugin/v1/submit', { method: 'POST', body: JSON.stringify( { /* 表单数据 */ } ), } ); // 导航回同一页面、绕过缓存 // 以反映更新后的内容。 const { actions: routerActions } = yield import( '@wordpress/interactivity-router' ); yield routerActions.navigate( window.location.href, { force: true, } ); }, }, } );

缓存本体就是 index.ts 中的const pages = new Map< string, Promise< Page | false > >()——一个 URL 路径到 Promise 的 Map,navigate内部用Promise.race([pages.get(pagePath), timeoutPromise])等待就绪或超时。

路由区域

路由区域是路由器知道如何在导航时更新的页面区域,它告诉路由器"这就是页面间导航时应变化的内容"的边界。

定义路由区域:在元素上同时添加data-wp-router-regiondata-wp-interactive(如设置路由区域所述)。属性值是区域的唯一标识符,有两种写法:

  1. 简单字符串:

    <div ><div ><!-- 这个页头不在任何路由区域内 --> <header>const { state } = store( 'myShop', { state: { get cartCount() { // 响应导航期间服务端状态变化。 return getServerState().cartCount; }, }, } );

    那么每当导航带来新的cartCount服务端值,购物车图标就会更新——尽管页头本身在任何路由区域之外。因为getServerState()建立了对服务端提供状态的响应式订阅,该订阅在每次导航时都会更新。这个模式对需要跨导航与服务器数据保持同步、又不必位于路由区域之内的全局 UI 元素非常有用;同样地,getServerState()也可用于同步路由区域内交互元素的state(见处理服务端状态更新)。

    CSS 处理

    客户端导航中较棘手的方面之一是管理 CSS 样式表。不同页面可能需要不同样式,路由器必须保证每页的样式正确生效——既不引起无样式内容闪烁,也不破坏 CSS 级联顺序。

    CSS 级联顺序的挑战:CSS 规则按特定顺序应用,两条规则特异性相同时,文档中靠后的那条"胜出"。这意味着 HTML 中<link><style>元素的顺序很重要。若路由器只是把新样式表追加到文档末尾,可能无意中改变哪些规则占优,引发视觉 bug。例如:A 页有base.csstheme.css,B 页有base.csscomponents.csstheme.css。从 A 导航到 B 时,路由器必须把components.css插在base.csstheme.css之间——而不是末尾。否则theme.css中用于覆盖components.css的规则将失效。

    样式如何被提取与准备:抓取页面时,路由器提取所有样式相关元素——<link rel="stylesheet">标签与内联<style>块。每个样式元素以其属性组合(<link>标签主要是href)或其内容哈希(内联<style>块)来识别。路由器将提取的样式与当前页面文档中已有的样式比较,样式落入三类:已存在(当前页已加载,准备阶段无需动作);新增(当前页不存在,需要添加);不再需要(当前页有、目标页没有,导航时将被禁用)。

    预加载新样式而不应用:对新样式表,路由器面临两难——既要在展示新页内容前确保样式完全加载好(防止无样式闪烁),又不能在用户仍看当前页时应用它们。解法是把新<link>元素的media属性设为阻止其生效的值。路由器使用media="preload",告诉浏览器"此样式表不适用于任何媒体类型"——实质上禁用它,同时允许浏览器下载并解析。这样添加后浏览器立即开始下载 CSS 文件,路由器通过监听load事件跟踪每个样式表何时加载完毕,从而能等新样式全部就绪后再继续导航。

    用最短公共超序列(SCS)算法维护级联顺序:插入新样式表时必须保持正确的级联顺序。路由器基于"求两个序列的最短公共超序列(Shortest Common Supersequence)"的算法实现,代码位于 packages/interactivity-router/src/assets/scs.ts,并有配套测试 scs.test.ts。

    给定当前页样式表(序列 X)与目标页样式表(序列 Y),SCS 算法找到同时以 X、Y 为子序列且保持二者内部顺序的最短序列,从而精确告诉路由器新元素插在哪里、保留哪些既有元素。例如:

    • 当前页样式(X):[A, C, D]
    • 目标页样式(Y):[A, B, C, E]
    • 最短公共超序列:[A, B, C, D, E]

    算法据此判定:A 与 C 原位保留,B 插入 A 与 C 之间,D 保持在 C 之后,E 插入末尾。该方法确保:两页都有的样式表保持正确相对顺序;新样式表被插到能维护级联正确性的位置;DOM 操作数量最少。

    导航时激活与停用样式navigate()真正渲染新页面时,路由器开关样式表:激活——对属于目标页的每个样式表,恢复原始media属性(撤销预取阶段设置的media="preload"覆盖)并设sheet.disabled = false,浏览器即应用这些样式;停用——对当前页有而目标页没有的样式表,设sheet.disabled = true,禁用样式而不从 DOM 移除元素。保留已停用的样式元素(而非删除)的好处是:用户导航回去时可快速重新激活——样式早已加载并解析,只需重新启用。

    脚本模块处理

    Interactivity API 使用脚本模块实现交互行为。路由器必须确保导航到新页面时所需脚本模块被加载并执行。

    识别客户端导航应加载的脚本模块:并非所有脚本模块都应在客户端导航时加载——有的只服务于管理后台,或只在首次页面加载时有用。如开始使用一节所述,WordPress 用data-wp-router-options属性标记哪些脚本模块在导航时应加载:

    <script type="module" src="/wp-content/plugins/my-plugin/view.js" ><script type="importmap"> { "imports": { "@wordpress/interactivity": "/wp-includes/js/dist/interactivity.min.js", "@wordpress/interactivity-router": "/wp-includes/js/dist/interactivity-router.min.js" } } </script>

    路由器抓取新页面时,从该页提取 import map 并与当前页的 import map 合并新映射,确保在脚本集合不同的页面间导航时,脚本模块仍正确解析依赖。

    预载脚本模块及其依赖:预载需要解析完整的依赖树——单个入口模块可能依赖几十个其他脚本模块,后者又依赖更多。路由器执行递归依赖解析:1)获取每个入口脚本模块的源码;2)解析源码找出所有import语句;3)用 import map 解析每个 import 的模块说明符;4)递归获取并解析每个依赖;5)直到依赖树中所有脚本模块都被获取。路由器还会避免冗余工作:若某脚本模块已被初始页面加载过(出现在初始 import map 中),就不再重复获取——浏览器已有缓存。

    处理 import 时序:一个微妙但重要的点——脚本模块代码不应在导航真正发生前执行。路由器需要代码就绪(避免导航时的延迟),但不希望用户还在看当前页时它就运行。路由器通过转换抓取到的脚本模块实现这一点:把源码改写为使用blob URL(数据直接嵌在 URL 中)并缓存这些转换后的模块;导航发生时用动态import()执行缓存的模块。由于浏览器模块系统按 URL 缓存模块,多次 import 同一 blob URL 返回同一模块实例——即使多条代码路径都去 import 它,每个脚本模块也只会执行一次。

    导航时脚本模块执行navigate()渲染新页面时,import 该页所有已预载的脚本模块:

    // 简化的概念示意。 for ( const moduleInfo of page.scriptModules ) { await import( moduleInfo.blobUrl ); }

    每个脚本模块的顶层代码随后运行,通常包括store()调用以注册 actions、callbacks 与 state。由于 Interactivity API 的 store 是全局且可叠加的,这些注册会与初始页面加载时已有的 store 定义合并。

    服务端状态与上下文

    交互元素常常需要来自服务器的数据——配置值、数据库内容、用户偏好等。Interactivity API 提供三种机制:全局状态、本地上下文与 config。客户端导航期间,这些服务端数据需要从新页面中提取并可供客户端代码使用。

    服务端数据如何嵌入页面:WordPress 渲染含交互元素的页面时,把服务端提供的数据嵌入特殊<script>标签:

    <!-- 全局状态 --> <script type="application/json" id="wp-script-module-data-@wordpress/interactivity" > { "state": { "myPlugin": { "cartItemCount": 3 } }, "config": { "myPlugin": { "userLoggedIn": true } } } </script>

    本地上下文则直接嵌入元素的data-wp-context属性:

    <div >import '@wordpress/interactivity-router/full-page';

    整页客户端导航本质上是区域式导航的一个特例——只有一个覆盖整页的区域。由于它替换全部内容,页面上的每个交互元素都必须使用 Interactivity API(而非 jQuery 或其他库),客户端导航才能正确工作。

    注意:该特性是实验性质、仍在积极开发中,在部分场景下可能无法正常工作。试用时如遇问题,请在 Gutenberg 官方仓库提交 issue 反馈,社区也欢迎贡献。

    小结

    客户端导航的接入路径可以概括为:在 packages/create-block-interactive-template 脚手架生成的client-side-navigation变体基础上,按"声明依赖 → 标记loadOnClientNavigation→ 布局data-wp-router-regionnavigate()/prefetch()接线"四步落地;遇到滚动焦点、跨页列表、动态区域、自定义错误处理等进阶需求,分别用 action 内显式处理、data-wp-keyattachTohtml选项解决。理解 packages/interactivity-router/src/index.ts 的页缓存与竞态保护、scs.ts 的 CSS 级联维护、script-modules.ts 的依赖树预载,以及服务端状态"客户端优先"的合并原则,能让你在排查导航异常与优化体验时有据可依。

    【免费下载链接】gutenbergThe Block Editor project for WordPress and beyond. Plugin is available from the official repository.项目地址: https://gitcode.com/GitHub_Trending/gu/gutenberg

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/20 9:04:54

抓词GEO靠谱吗?从官方资料、技术实力与行业口碑多角度解析

随着生成式AI逐渐成为用户获取信息的新入口&#xff0c;GEO工具开始受到企业关注。面对市面上层出不穷的GEO服务商&#xff0c;企业最关心的问题往往是&#xff1a;这款工具到底靠不靠谱&#xff1f;本文从产品功能完整性、技术工作流设计、计费透明度以及适用场景四个维度对抓…

作者头像 李华
网站建设 2026/9/20 9:04:55

ESB企业服务总线平台落地:架构拆解、注册表与灰度发布

简介&#xff1a;这份资源为企业服务总线&#xff08;ESB&#xff09;平台建设方案文档&#xff0c;面向企业架构师、集成开发人员与信息化项目负责人&#xff0c;用于解决多异构系统、应用与服务之间的集成难题&#xff0c;帮助实现业务流程自动化、数据交换与统一服务治理。文…

作者头像 李华
网站建设 2026/9/19 5:08:28

GPS+视日轨迹法实现高精度太阳能追光控制

简介&#xff1a;本资源是一份面向自动化、新能源与嵌入式系统方向本科生及工程实践者的专业设计文档&#xff0c;聚焦太阳能高效利用场景&#xff0c;解决传统固定式光伏板光能捕获率低的核心问题。文档详细阐述了基于GPS定位的自动追光系统整体架构&#xff0c;涵盖视日运动轨…

作者头像 李华
网站建设 2026/9/19 5:05:34

pigz离线安装与实战:多线程压缩让日志归档快数倍

前阵子接到一个运维需求&#xff1a;内网一台服务器要归档历史日志&#xff0c;压缩软件只有系统自带的 gzip&#xff0c;单线程压缩几个 GB 的文本文件&#xff0c;速度慢到让人怀疑人生。查了一圈发现最适合干这活的工具是 pigz——Gzip 的多线程并行实现&#xff0c;能直接把…

作者头像 李华
网站建设 2026/9/19 5:18:31

Migration progress: `<dir>`

Migration progress: <dir> 【免费下载链接】sanity Sanity Studio – Rapidly configure content workspaces powered by structured content 项目地址: https://gitcode.com/GitHub_Trending/sa/sanity Scope: <dir> v5 alias ui5 <date> Compon…

作者头像 李华