Navigation API
发布于 更新于
一句话: 根据 MDN 的说明,“Navigation API 提供了发起、拦截和管理浏览器导航操作的
能力”——它是 History API 与 window.location 的继任者,专门面向单页应用(SPA)的需求。
该 API 通过 Window.navigation 访问,根据 MDN 的说明,它“返回一个全局 Navigation
对象的引用”——每个 window 都有自己独立的实例。其中最重要的事件是 navigate,根据
MDN 的 Navigation API 概览页面,该事件“在任意类型的导航发起时触发,这意味着你可以从
一个中心位置控制所有页面导航,非常适合 SPA 框架中的路由功能”。MDN 的 navigate_event
参考文档对同一事件的描述更为具体:“在任意类型的导航发起时触发,使你能够按需拦截”。
根据 MDN 的 browser-compat-data,Navigation 接口自 Chrome 102 起受支持(Chrome for
Android 同样自 102 起),自 Firefox 147 起受支持,自 Safari 26.2 起受支持(Safari on
iOS 同样自 26.2 起)——兼容性数据中 Edge、Opera、Samsung Internet 的条目标记为
"mirror",意味着它们的支持数据是根据各自对应的上游 Chromium 版本推算得出的,而不是
直接给出的数值;这并不代表这些浏览器与 Chrome 使用相同的版本号。
NavigateEvent 接口本身与之同期上线,同样自 Chrome 102 起;只有其当前名称的
intercept() 方法稍晚才出现,即 Chrome 105(此前存在一个名称不同的方法
transitionWhile,自 Chrome 102 起存在,直到 Chrome 108 被移除)。Firefox 147 与
Safari 26.2 则是将 NavigateEvent 与 intercept() 一并上线。
监听 navigate 事件并调用 intercept(),用自定义逻辑处理导航而不是整页加载。以下示例
假设页面的应用外壳(app shell)已经渲染了一个 <div id="article"></div> 容器供其更新:
navigation.addEventListener("navigate", (event) => { // 有些导航(例如跨源导航)无法被拦截——让浏览器按常规方式处理。 if (!event.canIntercept) { return; } // 不拦截 hash 导航或下载。 if (event.hashChange || event.downloadRequest !== null) { return; }
const url = new URL(event.destination.url);
event.intercept({ async handler() { renderArticlePagePlaceholder(); const articleContent = await getArticleContent(url.pathname); renderArticlePage(articleContent); }, });});
function renderArticlePagePlaceholder() { document.querySelector("#article").innerHTML = "<p>加载中…</p>";}
async function getArticleContent(pathname) { const response = await fetch(`/api/articles${pathname}`); return response.text();}
function renderArticlePage(html) { document.querySelector("#article").innerHTML = html;}根据 MDN 的 navigate_event 参考文档,调用 handler() 时 URL 已经更新,因此其自身
示例会先渲染占位内容,再在获取到真实内容后替换——上面展示的正是这一模式。
运行时检测与回退
Section titled “运行时检测与回退”在注册处理函数之前,需要同时检测 window 上的 navigation 与
NavigateEvent.prototype.intercept——根据上文的兼容性数据,window.navigation 从
Chrome 102 就存在,但 intercept() 直到 Chrome 105 才上线,因此只检测 window 会让
Chrome 102–104 误判为支持,并在首次导航时抛出 TypeError。不支持时应回退为真正可用的
链接跳转与 History API 导航。以下示例假设页面已存在 <div id="article"></div> 容器,
以及若干站内链接(例如 <a href="/articles/1">):
function supportsNavigationApi() { return ( "navigation" in window && typeof NavigateEvent !== "undefined" && typeof NavigateEvent.prototype.intercept === "function" );}
if (supportsNavigationApi()) { navigation.addEventListener("navigate", handleAppNavigation);} else { // 此处没有可用的 Navigation API——依赖常规的链接点击以及 // History API 的 pushState/popstate 来实现路由。 setupHistoryApiRouting();}
function handleAppNavigation(event) { if ( !event.canIntercept || event.hashChange || event.downloadRequest !== null || typeof event.intercept !== "function" ) { return; } const url = new URL(event.destination.url); event.intercept({ async handler() { const response = await fetch(`/api/articles${url.pathname}`); document.querySelector("#article").innerHTML = await response.text(); }, });}
function setupHistoryApiRouting() { // 拦截同源链接点击,改为推入新的 History 记录,而不是触发整页加载。 document.addEventListener("click", (event) => { const link = event.target.closest("a[href]"); if ( !link || link.origin !== location.origin || event.defaultPrevented || event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey ) { return; } event.preventDefault(); history.pushState(null, "", link.href); renderArticleFromPath(location.pathname); });
// 前进/后退导航时,渲染与地址栏匹配的文章。 window.addEventListener("popstate", () => { renderArticleFromPath(location.pathname); });
renderArticleFromPath(location.pathname);}
async function renderArticleFromPath(pathname) { const response = await fetch(`/api/articles${pathname}`); document.querySelector("#article").innerHTML = await response.text();}- 根据 MDN 的
navigate_event参考文档,应“在不该被拦截的导航上提前退出”——例如event.hashChange或event.downloadRequest的情况——并非所有navigate事件都 应该被拦截。 - Chrome 的
intercept()方法取代了此前一个已被移除的方法transitionWhile——针对 Chrome 102–107 编写的、使用transitionWhile的示例代码在当前浏览器上无法运行。 - 根据 MDN 的兼容性数据,
NavigateEvent/intercept()分别在 Firefox 147 与 Safari 26.2 才落地,明显晚于 Chrome——站点仍需要为低于这些版本的浏览器保留 History API 回退路径。 - 根据 MDN 的说明,
navigate事件是所有导航类型的中心事件,但并非所有导航都能被 拦截:根据 MDN 的NavigateEvent.canIntercept参考文档,跨源导航就是canIntercept为false的情况之一,因此处理函数必须检查它,并让浏览器按常规方式处理这类导航, 而不是尝试拦截它们。