pagehide 比 unload 更适合移动端状态保存,因其兼容 bfcache:persisted===true 表示页面将缓存,false 才需最终持久化;需配合 freeze、visibilityState 等事件及 navigator.sendBeacon 确保可靠性。
window.onpagehide 可以替代 onunload,但前提是你要清楚它只在页面可能进入 bfcache(back/forward cache)时才可靠触发——而 onunload 一触发,bfcache 就彻底失效。
移动端浏览器(Chrome Android、Safari iOS)普遍启用 bfcache,用户按返回键或切到其他 Tab 时,页面常被冻结而非销毁。onunload 会强制绕过 bfcache,导致每次返回都重刷页面;pagehide 则允许缓存,且能通过 event.persisted 判断是否真要“离开”还是“暂存”。
event.persisted === true:页面将被存入 bfcache,后续 pageshow 会立刻恢复 DOM 和 JS 执行上下文event.persisted === false:页面即将彻底卸载(如关闭 Tab、刷新、跳转外链),此时才是保存状态的最后机会onunload 在 Safari 中甚至不保证执行完异步操作(比如发一个 fetch),而 pagehide 配合 queueMicrotask 能多争取一点时间别直接在 pagehide 回调里无条件保存——那样会在每次进入 bfcache 时重复写入,浪费性能还可能覆盖有效数据。
event.persisted === false 时做最终持久化(如 localStorage 写入、上报埋点)!frozen && event.persisted,再配合 performance.now() 限频pagehide 的触发时机较激进(比如 App 切后台就触发),所以建议用 document.visibilityState === 'hidden' 做二次确认pagehide 触发时,页面可能已被冻结(尤其是 iOS Safari),很多操作会静默失败或被忽略:
alert、confirm、window.open —— 全部无效JSON.stringify),可能阻塞冻结流程fetch 和 XMLHttpRequest 在 persisted === true 场景下大概率被中止,改用 navigator.sendBeacon 发送小量关键数据setTimeout 或 Promise.then 的后续执行——任务队列会被暂停仅靠 pagehide 不够,iOS Safari 和新版 Chrome 会先触发 freeze 事件(页面 JS 执行被暂停),再发 pagehide。真实场景中,freeze 是更早、更确定的“即将失活”信号:
freeze 时立即调用 saveState(),并标记 frozen = true
pagehide 中检查 !frozen && !event.persisted,补一次保存(防 freeze 未触发的边界情况)pageshow + event.persisted 判断是否从缓存来,再决定是否 restore真正麻烦的是 persisted 在不同浏览器中行为不一致:Chrome 通常准确,Safari 却可能在某些后台切换场景下返回 false 却仍保留页面状态。别把它当唯一依据,搭配 visibilityState 和 performance.memory(如有)交叉验证更稳。