visibilitychange、beforeunload 与 keepalive、navigator.sendBeacon
七崽爱吃小饼干2026/03/04阅读 3
在前端开发中,处理页面生命周期和数据上报是保障用户体验与数据完整性的关键环节。visibilitychange、beforeunload、keepalive 与 navigator.sendBeacon 经常被组合使用,以应对“用户突然关闭页面导致请求中断”的问题。
以下是这四个概念的深度解析:
1. 页面可见性 API:visibilitychange
这是目前现代浏览器推荐用来监听页面可见状态变化的 API。
- 作用: 当用户切换标签页、浏览器最小化、或者屏幕锁定时,该事件会被触发。
- 对比旧方案: 它比
unload和beforeunload更可靠,因为页面隐藏时并不代表页面被卸载,这给了开发者更灵活的优化空间(例如暂停视频播放或停止轮询)。 - 使用方式:
javascript
document.addEventListener("visibilitychange", () => {
if (document.visibilityState === "hidden") {
console.log("页面已隐藏,准备上报数据...");
}
});
2. 卸载确认:beforeunload
这是一个传统的事件,用于在用户离开页面(刷新或关闭)时拦截用户。
-
作用: 触发一个提示框,询问用户是否真的要离开。
-
注意事项:
-
不要滥用: 现代浏览器对该事件的限制非常严,为了防止骚扰,只有在页面有实质性交互后才会触发提示。
-
异步限制: 不能在该事件中执行复杂的异步逻辑。
-
最佳实践: 仅用于表单未保存等关键场景,提示用户保存更改。
3. 数据上报利器:navigator.sendBeacon
这是专门为“在页面卸载时发送数据”而设计的异步 API。
- 痛点背景: 传统的
XMLHttpRequest或fetch在页面卸载时往往会被浏览器强行终止,导致上报失败。 - 原理: 浏览器会将请求放入一个高优先级的队列中,并在页面卸载后异步且可靠地完成该请求,不会阻塞页面的跳转或关闭。
- 代码示例:
javascript
window.addEventListener("visibilitychange", () => {
if (document.visibilityState === "hidden") {
navigator.sendBeacon("/log", JSON.stringify({ event: "page_close" }));
}
});
4. 资源缓存控制:keepalive
keepalive 是 fetch API 的一个属性。
-
作用: 当
keepalive设为true时,即便页面被卸载,该请求也会继续在后台发送。 -
与
sendBeacon的区别: -
sendBeacon只能发送 POST 请求,且对数据大小有限制。 -
keepalive允许发送任何类型的 HTTP 请求(GET/POST/PUT等),并支持更细致的Headers控制。它是对sendBeacon能力的有力补充。 -
代码示例:
javascript
fetch("/log", {
method: "POST",
body: JSON.stringify({ data: "..." }),
keepalive: true // 关键所在
});
总结对比表
| 特性 | visibilitychange | beforeunload | navigator.sendBeacon | keepalive (fetch) |
|---|---|---|---|---|
| 主要用途 | 监听可见性状态 | 拦截页面卸载 | 页面卸载时可靠上报 | 页面卸载时执行 HTTP 请求 |
| 是否阻塞 | 否 | 是 (提示用户) | 否 | 否 |
| 适用场景 | 暂停轮询、埋点触发 | 表单防丢失提醒 | 轻量级埋点数据上报 | 复杂 API 请求上报 |
开发者建议
如果你的目的是在用户关闭页面前将统计数据发送给后端:
- 优先使用
visibilitychange作为触发时机。 - 优先使用
navigator.sendBeacon进行简单数据上报(兼容性好)。 - 如果需要复杂的 Header 或非 POST 请求,使用带有
keepalive: true的fetch。 - 尽量避免使用
unload事件,因为它已被标准废弃,且会干扰浏览器对页面的“往返缓存(bfcache)”优化。