unload 事件将被正式废弃

Chrome 117 will start the process of deprecating the unload event handler. If your site uses these then you are strongly advised to read the dedicated post on deprecating unload for more details.

按照Chrome 117 版本的更新日志来看,Chrome 将正式废弃unload 事件

unload 事件介绍

当文档或一个子资源正在被卸载时,触发 unload 事件。

unload 被设计为在卸载文档时触发。理论上,它可用于在用户离开页面时随时运行代码,或者作为会话回调结束时运行代码。此事件不可冒泡(Bubbles)、不可取消(Cancelable),它在下面两个事件后被触发:

使用如:

1
2
3
window.addEventListener('unload', function (event) {
console.log('page unload.');
});

此事件最常使用的场景包括:

  • 保存用户数据:在离开页面之前保存数据。
  • 执行清理任务:在放弃页面之前关闭打开的资源。
  • 发送分析:在会话结束时发送与用户交互相关的数据。

废弃原因

普遍认为unload事件“不太可靠”,还会影响页面关闭速度(卸载页面时可能阻塞浏览器)。不可靠主要是桌面端和移动端的差异:

在桌面版 Chrome 和 Firefox 中,unload 事件的触发比较可靠,但它会对网站性能产生负面影响,因为它会阻止使用后退/前进缓存(bfcache)。

在移动浏览器中, unload 事件通常不会运行,因为标签页经常被置于后台然后被关闭。因此,移动浏览器选择优先使用后退/前进缓存而不是 unload 事件,这使得 unload 事件在移动设备上更不可靠。(Safari 在桌面版也使用了这样的行为。)

Chrome 团队认为,在桌面版也采用移动版的优先使用后退/前进缓存而不是 unload 事件的模式会破坏桌面版的可靠性,因为过去在 Chrome(和 Firefox)中 unload 事件是比较可靠的。相反,Chrome 的目标是完全移除 unload 事件。在实现这一目标之前, 对于那些选择退出弃用处理的用户,unload 事件在桌面版仍将保持可靠。

unload 事件给了应用生命周期中一种错误的控制感,这与我们在现代计算世界中浏览网页的方式越来越不匹配。

移动操作系统经常冻结或卸载网页以节省内存,现在桌面浏览器也越来越多地出于同样的原因这么做。即使没有操作系统的干预,用户自己也经常切换标签页并关闭旧的标签页,而不会“正式离开页面”。

unload 事件作为过时事件移除,意味着我们作为 web 开发者需要确保我们的编程范式与现实世界相匹配,而不是依赖那些不再适用的过时概念。

p-2

替代方案

以下事件代替 unload 事件:

  • visibilitychange: 用于确定页面可见性发生变化时。当用户切换标签页、最小化浏览器窗口或打开新页面时,会触发此事件。可以在 hidden 状态下保存应用和用户数据。
  • pagehide: 用于确定用户已经导航离开该页面时。当用户导航离开页面、重新加载页面或关闭浏览器窗口时,会触发此事件。如果页面只是被最小化或切换到另一个标签页,pagehide 事件不会被触发。请注意,由于 pagehide 事件不会使页面失去后退/前进缓存的资格,在此事件触发后,页面可能会被恢复。如果在此事件中清理任何资源,那么在页面恢复时可能需要重新加载这些资源。

beforeunload 事件与 unload 事件有略微不同的使用案例,因为它是一个可以取消的事件。它通常用于在用户导航离开时警告未保存的更改。如果后台标签被关闭,这个事件也不可靠。建议限制使用 beforeunload 事件,仅在有条件时添加它。大多数情况下,建议使用上述事件来代替 unload 事件

visibilitychange

当用户切换选项卡、最小化浏览器窗口或打开新页面时,都会触发这个事件。当我们需要在页面不可见是做点操作时,可以判断这个 document.visibilityState 是否为 hidden

1
2
3
4
5
6
7
8
9
document.addEventListener('visibilitychange', function () {
if (document.visibilityState === 'visible') {
// 页面变为可见状态时的操作
console.log('page is visible');
} else if (document.visibilityState === 'hidden') {
// 页面变为不可见状态时的操作
console.log('page is unvisible');
}
});

pagehide

会在用户点击跳转其他链接、前进或后退按钮,或关闭浏览器选项卡时触发,也能够用来确定用户什么时候离开界面:

1
2
3
4
window.addEventListener('pagehide', function (event) {
console.log('page will hide or exit');
// 执行相应的操作
});

pagehide 不会像 unload 一样让页面不符合bfcache (浏览器的前进,后退,缓存操作)的条件。

如果来不及处理怎么办

以下选项允许你启用或禁用 unload 处理程序,以测试在没有它们的情况下你的网站会如何工作,以便为即将到来的废弃做准备。有不同类型的策略:

如 Permissions Policy:

递归地禁用当前页面及其所有子 iframeunload事件,可以添加如下 Header

1
Permissions-Policy: unload=()

递归地禁用当前页面及其所有子 iframeunload事件,但是想保留部分页面的:

1
Permissions-Policy: unload=(https://www.example.com)

只是上知道网站上是否有调用 unload 事件,但不进行拦截:

1
Permissions-Policy-Report-Only: unload=()

如何避免(检测)

Lighthouse

Lighthouse 有一个no-unload-listeners审计,如果页面上的任何 JavaScript(包括来自第三方库的 JavaScript)添加了一个 unload 事件监听器,它会警告开发人员。

p-3

Chrome DevTools

Chrome DevTools 有一个 back-forward-cache 审计,可以帮助你识别可能阻止页面使用后退/前进缓存的问题,包括使用 unload 事件处理程序。

要测试后退/前进缓存,请执行以下步骤:

  • 在你的页面上,打开 DevTools,然后转到 Application > Background services > Back/forward cache
  • 点击 Test back/forward cache。Chrome 会自动带你到 chrome://terms/ 然后回到你的页面。或者,你可以点击浏览器的后退和前进按钮。
    如果你的页面不符合后退/前进缓存的条件,Back/forward cache 标签会显示一个问题列表。在 Actionable 下面,你可以看到是否使用了 unload 事件:

p-4


相关链接