Chrome 105 在 Navigation API 的 NavigateEvent
上引入了两个新方法(在 102 年引入),以改进在实践中证明有问题的方法。让开发者按照导航操作后控制状态的 intercept()
取代了后来被证明不易使用的 transitionWhile()
。scroll()
方法会滚动到网址中指定的锚点,并替换了不适用于所有类型的导航的 restoreScroll()
。
在本文中,我将说明这两种操作所存在的问题,以及这些新方法如何解决这些问题。
NavigateEvent.transitionWhile()
在 Chrome 102 中随 Navigation API 引入的 NavigateEvent.trasitionWhile()
方法可拦截单页应用中的客户端过渡的导航。它的第一个参数是一个 promise,可向浏览器和 Web 应用的其他部分表明其已完成。
这种做法在实践中效果不佳。请考虑以下常见的编码模式:
event.transitionWhile((async () => {
doSyncStuff();
await doAsyncStuff();
})());
该代码在功能上等同于以下代码。这会导致在 API 获知开发者打算拦截导航之前,部分导航元素就已运行。
doSyncStuff();
event.transitionWhile((async () => {
await doAsyncStuff();
})());
可能导致应用变得混乱的一个示例是滚动恢复逻辑,即在 DOM 更改之后(而不是之前)捕获滚动位置。
具体变化
为了替换 transitionWhile()
,当前规范引入了 NavigateEvent.intercept()
。除了 transitionWhile()
支持的 focusReset
和 scrollRestoration
属性之外,新方法还会使用处理程序。新的处理程序始终在提交导航后运行,并且已捕获滚动位置等内容,从而避免 transitionWhile()
问题。
transitionWhile()
方法仍然可用,但已被弃用,并将从 Chrome 108 中移除。
使用 Interface()
NavigateEvent.intercept()
的限制与 transitionWhile()
相同,因为无法对所有导航事件调用它。无法拦截跨源导航和跨文档遍历。这样做会抛出 "SecurityError"
类型的 DOMException
。
如需使用 intercept()
,只需在调用时传递自定义处理程序即可。
navigation.addEventListener("navigate", event => {
event.intercept({
async handler() {
doSyncStuff();
await doAsyncStuff();
}
});
});
NavigateEvent.scroll()
从页面顶部到锚点(称为从 /a
移动到 /a#id
)等导航操作完全由浏览器处理,即使在单页应用中也是如此。但导航到其他“网页”上的锚点(/a
到 /b#id
)对多页应用来说很简单,而对于单页应用来说却比较复杂。应用必须使用 NavigateEvent.transitionWhile()
拦截到 /b#id
的导航,然后调用 NavigateEvent.restoreScroll()
以使锚点进入视图。如上所述,目前这很难做到。
具体变化
在单页应用中,您现在可以控制浏览器是否处理滚动至锚点,或者是否处理您的代码。
使用 scroll()
默认情况下,在拦截处理程序执行完毕后,浏览器会尝试自动处理滚动。如果您想自行处理滚动,请将 scroll
设为 "manual"
,然后在浏览器应尝试设置滚动位置时调用 NavigateEvent.scroll()
。
navigation.addEventListener("manual", event => {
scroll: "manual",
event.intercept({
async handler() {
doSyncStuff();
// Handle scrolling earlier than by default:
event.scroll();
await doAsyncStuff();
}
});
});
restoreScroll()
方法仍然可用,但已被弃用,并将从 Chrome 108 中移除。
总结
我们希望能尽快更新有关 Navigation API 的文章。在此期间,此 API 的规范包含大量专门面向 Web 开发者的信息。