应使用 pagehide 替代 unload 以适配 bfcache,通过 event.persisted 区分缓存(true)与卸载(false)场景;配合 visibilitychange 做前置资源冻结;关键数据上报必须用 sendbeacon 确保发出。

用 window.onpagehide 替代 unload 并不是简单替换,而是针对现代浏览器(尤其是移动端)的缓存机制做适配。关键在于:页面可能不卸载,只是被放入 bfcache(back/forward cache),此时 unload 根本不会触发,而 pagehide 会,并且能告诉你页面是否将被缓存。
理解 persisted 属性是核心
pagehide 事件对象带有一个 persisted 布尔值,它直接反映浏览器对当前页面的缓存意图:
-
event.persisted === true:页面即将进入 bfcache(比如用户点后退按钮),此时 JS 执行环境仍完整,不需要立即持久化状态,可跳过保存逻辑; -
event.persisted === false:页面确定要卸载(关闭、刷新、跳转站外等),这是唯一需要执行最终状态固化的地方。
搭配 visibilitychange 做前置感知
pagehide 只在离开瞬间触发一次,但用户可能早已离开视线。若需更早响应(如暂停视频、降级轮询),应配合 visibilitychange:
- 监听
document.visibilityState,当变为'hidden'时,可主动冻结非必要资源; - 注意:
visibilitychange无法区分“最小化”和“关页”,也不能保证后续一定触发pagehide(比如进程被系统杀死); - 它适合轻量预处理,重状态保存仍应落在
pagehide的persisted === false分支中。
用 sendBeacon 确保数据发出
pagehide 的执行时机非常靠后,常规 fetch 或 XMLHttpRequest 极易被中断。必须使用:
-
navigator.sendBeacon(url, data):异步、无回调、浏览器保证发送(即使页面已销毁); - 数据建议用
URLSearchParams或Uint8Array,避免序列化失败; - 不要依赖返回值或错误处理——它设计上就是“发了就不管”。如需确认,应在服务端记录并提供查询接口。
避免常见陷阱
几个高频出错点:
- 把
pagehide当成beforeunload用:后者可弹提示框阻断跳转,pagehide完全不可阻断,也不该尝试; - 在多个模块重复绑定
pagehide:推荐统一注册一次,通过事件委托或状态管理协调各模块的保存逻辑; - 忽略移动端 WebView 特性:部分安卓 WebView 对
pagehide支持不稳定,可加降级逻辑(如监听visibilitychange+ 定时快照)。











