@wordpress/interactivity-router 包支持在不重新加载整个页面的情况下从其他页面加载内容。目前仅支持“基于区域”的模式。“客户端侧导航”仍处于实验阶段。

该包定义了一个具有 core/router 命名空间的 Interactivity API store,暴露了用于处理客户端侧导航的状态和两个操作:navigate 和 prefetch。

@wordpress/interactivity-router 包在 WordPress Core v6.5 版本中引入。这意味着此包已捆绑在高于 v6.5 版本的 WordPress Core 中。

有关客户端侧导航工作原理的综合指南(包括入门、块兼容性和高级用例),请参阅 客户端侧导航指南。要了解如何确保您的块与客户端侧导航兼容,请参阅 客户端侧导航兼容性指南。查看 Block Editor 手册中的 Interactivity API 参考文档,了解更多关于 Interactivity API 的信息。

用法

该包旨在在交互块的 view.js 文件中动态导入。这样做是为了减少初始页面加载时的 JS 包大小。

/* view.js */

import { store, withSyncEvent } from '@wordpress/interactivity';

// 这是在您的块中通常使用 navigate() 操作的方式。
store( 'my-namespace/myblock', {
    actions: {
        // 需要使用 withSyncEvent() 实用程序,因为 preventDefault() 需要同步事件访问。
        goToPage: withSyncEvent( function* ( e ) {
            e.preventDefault();

            // 我们动态导入该包以减少初始 JS 包大小。
            // 异步操作被定义为生成器,因此必须使用 `yield` 调用 import()。
            const { actions } =
                yield import( '@wordpress/interactivity-router' );
            yield actions.navigate( e.target.href );
        } ),
    },
} );

现在,您可以在块的 view.js 文件中调用 actions.navigate() 以导航到不同的页面,或者将其传递给 data-wp-on--click 属性。

加载后,此包向 core/router store 添加了以下状态和操作:

const { state, actions } = store( 'core/router', {
    state: {
        url: window.location.href,
        // `navigating` 和 `initiator` 也是 store 状态的一部分,
        // 但它们由 `navigate()` 写入,而不是在此处声明,
        // 因此在第一次导航之前读取均为 `undefined`。
    },
    actions: {
        *navigate(href, options) {...},
        prefetch(url, options) {...},
    }
})

除了当前 URL 之外,store 的状态还描述了导航本身:是否正在飞行中(state.navigating)以及是谁发起的(state.initiator)。见下文 State。

核心“查询循环”块使用了此包来提供 基于区域的导航。

指令

data-wp-router-region

它定义了在导航时更新的区域。它需要一个唯一的 ID 作为值,并且必须与 data-wp-interactive 一起使用以获得正确的命名空间。路由器区域可以放置在交互区域内的任何位置,包括嵌套的交互元素。

当将 data-wp-router-region 添加到具有 data-wp-interactive 的父元素内的子元素时,始终也在子元素上包含 data-wp-interactive。这是路由器区域正常工作的必要条件。

该值可以是带有区域 ID 的字符串,也可以是包含 id 和可选 attachTo 属性的 JSON 对象。

示例:

<div data-wp-interactive="myblock" data-wp-router-region="main-list">
    <ul>
        <li><a href="/post-1">Post 1</a></li>
        <li><a href="/post-2">Post 2</a></li>
        <li><a href="/post-3">Post 3</a></li>
    </ul>
    <a data-wp-on--click="actions.navigate" href="/page/2">Page 2</a>
</div>

attachTo 属性是一个 CSS 选择器,指向应渲染新路由器区域的父元素。这对于可能在初始页面上不存在但在后续页面上存在的区域(如模态框或覆盖层)非常有用。

在页面之间导航时:

  • 如果区域存在于当前页面和新页面上,则更新其内容。在这种情况下忽略 attachTo。
  • 如果新页面上存在没有 attachTo 的区域但当前页面上不存在,则不会将其添加到 DOM 中。
  • 如果新页面上存在带有 attachTo 的区域但当前页面上不存在,则创建该区域并将其附加到 attachTo 中指定的父元素。
  • 如果当前页面上存在区域但新页面上不存在,则将其从 DOM 中移除。在这种情况下忽略 attachTo。

带有 attachTo 的示例:

<div
    data-wp-interactive="myblock"
    data-wp-router-region='{ "id": "myblock/overlay", "attachTo": "body" }'
>
    I'm in a new region!
</div>

操作

navigate

导航到指定页面。

此函数规范化传递的 href,如有必要则获取页面 HTML,并更新在新页面中内容发生变化的任何交互区域。它还会在浏览器会话历史记录中创建一个新的条目。

参数

navigate( href: string, options: NavigateOptions = {} )
  • href: 页面 href。
  • options: 选项对象。
    • force: 如果为 true,则强制重新获取 URL。navigate() 始终缓存页面,因此如果之前已导航到该页面,将使用该缓存。默认为 false。
    • html: 用作替代请求 URL 获取的 HTML 字符串。
    • replace: 如果为 true,则替换浏览器会话历史记录中的当前条目。默认为 false。
    • timeout: 导航中止前的时间,以毫秒为单位。默认为 10000。
    • loadingAnimation: 是否在导航期间显示动画。默认为 true。
    • screenReaderAnnouncement: 是否在导航期间向屏幕阅读器发布公告。默认为 true。
    • initiator: 标识谁发起了此导航,只要它在飞行中就会发布到 state.initiator。非空字符串按原样使用且始终优先。null 会抑制归属,因此 state.initiator 在整个过程中读取为 null。如果省略(默认值),则发起者源自环境指令作用域:调用操作的元素的最近封闭 data-wp-router-region 的 ID(包括该元素本身),如果没有则为 null。任何其他值(空字符串、数字、对象、false)被视为 null,并在启用 SCRIPT_DEBUG 时记录警告;它永远不会回退到推导发起者。

prefetch

预取传入 URL 的页面。该页面会被缓存并可用于导航。

该函数规范化 URL 并在内部存储获取承诺,以避免对进行中的请求触发第二次获取。

参数

prefetch( url: string, options: PrefetchOptions = {} )
  • url: 页面 url。
  • options: 选项对象。
    • force: 如果为 true,则强制再次获取 URL。
    • html: 用作替代请求 URL 获取的 HTML 字符串。

状态

所有三个属性都是响应式的,因此在指令或 watch() 回调中读取它们时会订阅其更改。

  • state.url (string):与当前 URL 同步。
  • state.navigating (boolean | undefined): 在客户端导航进行中时,该值为真。导航一旦进入客户端管道即发布开始信号,无需等待目标页面;结束信号在新内容提交到 DOM 后的后续帧中发布,且提交与结束之间的延迟是不确定的。如果导航在半途中回退到完整的页面加载,它会在浏览器替换文档期间保持为真值,并在 10 秒后文档仍然存在时重置为 false;如果导航在渲染过程中抛出异常,则正常结束,此时页面可能仅部分更新。在这两种情况下,结束信号均不承诺有新内容。loadingAnimation 和 screenReaderAnnouncement 选项均不影响此键:它们改变的是内置反馈,而非此键。
  • state.initiator (string | null | undefined): 标识谁启动了正在进行的导航——该值解析自 navigate() 的 initiator 选项,或者当导航没有可识别的发起者时为 null,例如后退/前进遍历、使用无指令范围发出的调用,以及从响应 state.navigating、state.initiator 或 state.url 的 watch() 内部发出的调用(此类调用不会继承其响应的导航的发起者)。导航的结束不会清除它:它保留该导航的值,直到下一次导航开始,或者直到执行重新加载文档的后退/前进遍历将其清除为 null。

由于在注册 store 时未声明 navigating 和 initiator,因此在首次客户端导航写入它们之前,两者读取均为 undefined,并且在路由器模块从未加载的页面上也是如此。请通过真值性来使用它们——例如 if ( state.navigating ) 和 state.initiator === myRegionId——而不是与 false 或 null 进行比较。请注意,像 data-wp-bind--aria-busy="state.navigating" 这样的绑定,当值为 undefined 时根本不会渲染任何属性,而不是 aria-busy="false"。

派生的 state.initiator 携带区域的 id,而不是原始的 data-wp-router-region 属性文本。对于 JSON 对象形式,它是 id 属性的值,会剥离 namespace:: 前缀,空或非字符串的 id 读取为 null 而非空字符串。请与赋予该区域的 id 进行对比,拼写需完全一致。

有关完整语义——包括涵盖哪些导航、哪些完全不产生过渡、重叠导航对两者的影响,以及从哪个指令读取它们——请参阅客户端导航指南中的响应导航生命周期。

安装

安装模块:

npm install @wordpress/interactivity-router --save

如果您在 WordPress 之外使用 Interactivity API,则只需要此步骤。

在 WordPress 内部,该包已捆绑在 Core 中。为确保其被排队加载,请将 @wordpress/interactivity-router 添加到脚本模块的依赖数组中。通常可以使用 wp-scripts 等工具自动完成此过程。

此外,此包假设您的代码将在 ES2015+ 环境中运行。如果您使用的环境对这些语言特性和 API 的支持有限或完全不支持,您应该在代码中包含 @wordpress/babel-preset-default 随附的 polyfill。

许可证

作为 Gutenberg 和 WordPress 项目一部分的 Interactivity API 提案是自由软件,并根据 GNU 通用公共许可证第 2 版或(根据您的选择)任何更高版本的条款发布。有关完整许可证信息,请参阅 LICENSE.md。