什么你还不会用navigation来管理导航?

navigation api是W3C提出的实验性方案,2023年成为正式标准草案,chrome、edge浏览器支持相当不错了。

提供了启动、拦截和管理浏览器导航操作的功能。它还可以检查应用程序的历史记录条目。这是对之前 Web 平台特性(例如历史记录 APIwindow.location 的继承并解决了它们的缺点,且专门针对单页应用程序(SPA)的需求。

  • 拦截导航:在用户触发导航(点击链接、前进/后退)时拦截并自定义处理。
  • 细粒度控制:管理导航队列、处理异步操作、动态更新页面内容。
  • 增强历史管理:更灵活地操作历史记录,支持复杂状态管理。

history api处理导航的缺点

  • 状态与 URL 分离: 状态(state 对象)与 URL 改变是绑定的,但获取和监听状态变化非常笨拙(依赖 popstate 事件,且该事件也响应浏览器按钮)。
  • 导航监听困难: 区分用户点击链接、表单提交、脚本触发导航、浏览器前进/后退非常困难。popstate 只响应某些导航(主要是同文档的历史切换)。
  • 缺乏对"导航"过程的控制: 无法优雅地拦截导航、显示加载状态、处理取消或失败。
  • 信息不完整: 难以获取导航的详细信息(如导航类型、目标 URL、发起者等)。
  • 操作历史条目不便: 遍历、修改或获取历史条目的详细信息很麻烦。
  • 只能导航同源路径

属性

activation

  • entry: NavigationHistoryEntry对象,表示当前导航页信息

    • id 导航唯一标识,始终代表特定的历史记录条目。

    • index 历史条目列表(即 Navigation.entries()返回的列表)中历史条目的索引,如果该条目未出现在列表中,则返回 -1

    • key 表示历史条目在条目列表中的位置,而不是条目本身。在整个会话历史中不变。 可用于traverseTo()导航到指定条目。

    • sameDocument 判断此历史记录条目与当前 Document 值属于同一个 document例如hash导航

    • url 此历史记录条目的绝对 URL 地址。

    • getState() 获取编程式导航传递的state数据。这是替代传统 history.state 的更易访问的方式。

    js 复制代码
    async function handleReload() {
      // 通过 reload() 更新现有状态
      await navigation.reload({
        state: { ...navigation.currentEntry.getState(), newState: 3 },
      });
    
      // 将当前状态打印到控制台
      const current = navigation.currentEntry;
      console.log(current.getState());
    }
    • dispose事件在条目不再是历史记录条目列表的一部分时触发。
  • from: NavigationHistoryEntry对象,原导航页信息。内部属性同上,如果导航到非同源页面将返回null。
  • navigationType: 表示导航类型的字符串。
    • push:导航到新位置,导致新条目被推送到历史列表中。
    • reloadNavigation.currentEntry 已重新加载。调用reload
    • replaceNavigation.currentEntry被替换为新的历史记录条目。此新条目将重用相同的 key,但分配不同的 id
    • traverse:浏览器从一个现有的历史记录条目导航到另一个现有的历史记录条目。通过traverseTo, back. forward导航。

canGoBack

是否可以在导航历史记录中向后导航,即 currentEntry 不是历史记录条目列表中的第一个。

canGoForward

是否可以在导航历史记录中向前导航,即 currentEntry 不是历史记录条目列表中的最后一个。

currentEntry

表示用户当前导航到的历史记录。即activation.entry

transition

表示正在进行的导航的状态,可用于跟踪正在进行的导航。如果当前没有正在进行的导航,则为 null

方法

back(options)

在导航历史记录中向后导航一个条目。如果当前导航在历史记录的第一个位置将会抛出错误。在调用前可以通过navigation.canGoBack进行判断是否可以导航。

  • options.info 导航传递的数据,可以在navigate 事件的事件对象中获取到。可以是任何数据类型。

返回值

  • committed 当可见 URL 发生改变并且新的 NavigationHistoryEntry 被创建时,将兑现的 Promise

  • finishedintercept() 处理器返回的所有 promise 都被兑现时,将兑现的 Promise。这相当于当 navigatesuccess 事件触发时,兑现 NavigationTransition.finished promise。

forward(options)

在导航历史记录中向前导航一个条目。参数和返回值介绍同上。如果当前导航在历史记录的最后一个位置将会抛出错误。在调用前可以通过navigation.canGoForward进行判断是否可以导航。

entries()

返回一个 NavigationHistoryEntry 对象数组,代表所有现有的历史导航记录条目。注意他只能获取和当前文档同源的历史条目。history.length会返回当前tab页面所有历史条目总和(不管同不同源)

导航到特定的 URL,更新历史记录条目列表中提供的任何状态。

  • url 要导航到的目标的 URL。
  • options.state 将在导航完成后存储在关联的 NavigationHistoryEntry 中,可通过 getState() 检索。存储在 state 中的任何数据都必须是可结构化克隆的。
  • options.info 导航传递的数据,可以在navigate 事件的事件对象中获取到。可以是任何数据类型。
  • options.history 设置此导航的历史记录行为的枚举值。可用的值包括:
    • auto:默认值;通常会执行 push 导航,但在特殊情况下会执行 replace 导航。
    • push:将会把新的 NavigationHistoryEntry 推送到历史记录条目列表中。
    • replace:将替换当前的 NavigationHistoryEntry

返回值

  • committed 当可见 URL 发生改变并且新的 NavigationHistoryEntry 被创建时,将兑现的 Promise

  • finishedintercept() 处理器返回的所有 promise 都被兑现时,将兑现的 Promise。这相当于当 navigatesuccess 事件触发时,兑现 NavigationTransition.finished promise。

reload(options)

重新加载当前 URL,同时更新历史记录条目列表中对应的条目的状态。

  • options.state 将在导航完成后存储在关联的 NavigationHistoryEntry 中,可通过 getState() 检索。存储在 state 中的任何数据都必须是可结构化克隆的。
  • options.info 导航传递的数据,可以在navigate 事件的事件对象中获取到。可以是任何数据类型。

traverseTo(key, options)

导航到由给定的 key 标识的 NavigationHistoryEntry

  • key 要导航到的目标的 NavigationHistoryEntry.key
  • options.info 导航传递的数据,可以在navigate 事件的事件对象中获取到。可以是任何数据类型。

返回值

  • committed 当可见 URL 发生改变并且新的 NavigationHistoryEntry 被创建时,将兑现的 Promise

  • finishedintercept() 处理器返回的所有 promise 都被兑现时,将兑现的 Promise。这相当于当 navigatesuccess 事件触发时,兑现 NavigationTransition.finished promise。

updateCurrentEntry(options)

更新 currentEntrystate;用于状态改变且与导航或重新加载无关的情况。类似于 history.replaceState

  • options.state 将在导航完成后存储在关联的 NavigationHistoryEntry 中,可通过 getState() 检索。存储在 state 中的任何数据都必须是可结构化克隆的。
js 复制代码
navigation.updateCurrentEntry({state:{name:'zh', age: 25}})
navigation.currentEntry.getState()

事件

currententrychange

Navigation.currentEntry 发生更改时触发。即可见 URL 已更改且 NavigationHistoryEntry已更新(state或其他数据更新)。

  • 同文档导航(例如 back(), forward(), traverseTo())。
  • 替换(即 navigate() 调用,并将 history 参数设置为 replace)。
  • 更改条目状态的其他调用(例如 updateCurrentEntry(),或 History API的 History.replaceState())。

可以在事件对象NavigationCurrentEntryChangeEvent中获取

  • from 导航来源的 NavigationHistoryEntry
  • navigationType 导致改变的导航的类型。

在发起任何类型的导航时触发,让你可以根据需要进行拦截。

js 复制代码
navigation.addEventListener('navigate', (event) => {
  // 检查是否可拦截(同源导航)
  if (!event.canIntercept) return;
  
  // 阻止默认刷新行为,也可阻止navigation.reload()
  // event.preventDefault();
  
  // 拦截导航过程
  event.intercept({
    handler: async () => {
      // 这里执行无刷新更新
    }
  });
});

该事件的事件对象NavigateEvent中提供了很多的属性和方法供我们对导航进行处理。

  • canIntercept 是否可以拦截导航(一般同源导航可以被拦截 )并重写其 URL。 何时可以进行拦截请看这里
  • destination 返回一个 NavigationDestination 对象,表示要导航到的目标。
  • downloadRequest 在导航为下载导航的情况下返回请求下载的文件的文件名。例如具有 download 属性的 <a> 或 <area> 元素。
  • formData 在导航为 POST 表单提交导航的情况下返回表示提交的数据的 FormData 对象,否则返回 null
  • hashChange 当前导航是否是修改hash的导航。
  • hasUAVisualTransition 在用户代理分派此事件之前已为此导航执行了可视化的过渡时返回 true,否则返回 false
  • info 返回启动导航操作传递的 info 数据值,如果没有传递 info 数据,则返回 undefined
  • navigationType 返回导航的类型(pushreloadreplacetraverse)。
    • push:导航到新位置,导致新条目被推送到历史列表中。
    • reloadNavigation.currentEntry 已重新加载。
    • replaceNavigation.currentEntry被替换为新的历史记录条目。此新条目将重用相同的 key,但分配不同的 id
    • traverse:浏览器从一个现有的历史记录条目导航到另一个现有的历史记录条目。
  • signal 返回一个 AbortSignal,如果导航被取消(例如,用户按下浏览器的"停止"按钮,或者另一个导航启动并因此取消正在进行的导航),该信号将被中止。可以在导航取消时阻止一些不必要的网络请求。
  • userInitiated 导航是否是由用户发起的(例如,通过单击链接、提交表单或按浏览器的"后退"/"前进"按钮)。
  • sourceElement 返回触发元素(a, area, input["submit"], button["submit"], form)。
  • intercept(options) 方法拦截此导航,将其转变为到目标 URL 的同一文档导航。
    • options.handler 定义导航处理行为的回调函数。这通常用于处理资源获取并返回 promise。
    • focusReset 定义导航的焦点行为。这可能接受以下值之一:
      • after-transition 一旦处理器返回的 promise 兑现,浏览器将聚焦具有 autofocus 属性的第一个元素,或者如果没有元素设置 autofocus,则聚焦 <body> 元素。这是默认值。
      • manual禁用默认行为。
    • scroll 定义导航的滚动行为。这可能接受以下值之一:
      • after-transition允许浏览器处理滚动,例如,如果 URL 包含片段,则滚动到相关片段标识符;如果重新加载页面或重新访问历史记录中的页面,则将滚动位置恢复到上次的同一位置。这是默认值。
      • manual禁用默认行为。
  • scroll() 方法可用于手动触发浏览器在导航过程中执行的滚动行为,如果希望该行为在导航处理完成之前发生,则可以调用此方法。

在导航失败后触发。

  • 导航时出现错误
  • 调用导航api传递参数错误
  • 网络请求失败
  • 用户主动中止
js 复制代码
// 拦截处理抛出错误
navigation.addEventListener('navigate', (event) => {
  event.intercept({
    handler: () => {
      throw new Error('权限校验失败'); // 触发 navigateerror
    }
  });
});

// 网络请求失败
navigation.navigate('/unreachable-page');

// 用户主动中止
const controller = new AbortController();
navigation.navigate('/cancelable', { 
  signal: controller.signal 
});
controller.abort(); // 触发 AbortError

在导航成功完成后触发。

  • 导航完成
  • 拦截处理函数完成 (intercept)
  • 目标资源加载完成

应用

替代history api, hash模式实现前端路由。

js 复制代码
  <a href="/zh">zh</a>
  <a href="/llm">llm</a>
  <div id="container">我是默认页面</div>
  <script>
    const container = document.getElementById("container");
    const aEls = document.getElementsByTagName("a");
    const changeContent = (event) => {
      console.log("event", event)
      if (!event.canIntercept) return;

      event.intercept({
        async handler () {
          switch (location.pathname) {
            case "/zh":
              container.innerHTML = "我是zh页面"
              break;

            case "/llm":
              container.innerHTML = "我是llm页面"
              break;

            default:
              container.innerHTML = "我是其他页面"
              break;
          }
        },
      });

    }

    navigation.addEventListener("navigate", changeContent)
  </script>

兼容性

参考

## 往期年度总结

往期文章

专栏文章

🔥如果此文对你有帮助的话,欢迎💗关注 、👍点赞 、⭐收藏✍️评论, 支持一下博主~

公众号:全栈追逐者,不定期的更新内容,关注不错过哦!

相关推荐
Carlos_sam37 分钟前
OpenLayers:封装一个自定义罗盘控件
前端·javascript
前端南玖1 小时前
深入Vue3响应式:手写实现reactive与ref
前端·javascript·vue.js
wordbaby1 小时前
React Router 双重加载器机制:服务端 loader 与客户端 clientLoader 完整解析
前端·react.js
tactfulleaner1 小时前
手撕MHA、MLA、MQA、GQA
面试
itslife1 小时前
Fiber 架构
前端·react.js
3Katrina1 小时前
妈妈再也不用担心我的课设了---Vibe Coding帮你实现期末课设!
前端·后端·设计
hubber1 小时前
一次 SPA 架构下的性能优化实践
前端
可乐只喝可乐2 小时前
从0到1构建一个Agent智能体
前端·typescript·agent
Muxxi2 小时前
shopify模板开发
前端
Yueyanc2 小时前
LobeHub桌面应用的IPC通信方案解析
前端·javascript