pagehide 事件的 persisted 属性是判断页面是否进入 bfcache 的核心依据:true 表示冻结暂存,应暂停轮询、冻结 ui;false 表示彻底卸载,需立即存盘、上报日志、清理数据。

直接用 window.onpagehide 替代 unload 是可行的,但关键不在“替换”本身,而在理解它何时真正可靠——它只在页面可能被浏览器缓存(bfcache)时才稳定触发,而 unload 一执行,bfcache 就被废掉。所以不是简单换一个事件名,而是要按移动端真实生命周期来设计存盘逻辑。
区分两种退出场景:缓存暂存 vs 彻底卸载
pagehide 事件对象带有一个 persisted 属性,它是判断是否该存盘的核心依据:
-
event.persisted === true:页面将进入 bfcache(比如用户按返回键、切到其他 Tab、锁屏),DOM 和 JS 状态会被冻结保留,下次
pageshow会瞬间恢复。此时不应保存草稿或上报离开行为,只需冻结 UI、暂停轮询、释放非必要资源。 -
event.persisted === false:页面即将彻底销毁(如关闭 Tab、跳转外链、手动刷新),这才是最后一次可靠的存盘机会,应立即调用
saveDraft()、发送停留时长日志、清理临时数据。
避免常见陷阱:冻结中不能做的事
页面进入 bfcache 前可能已被冻结(尤其 iOS Safari),很多操作会静默失败:
- 不要调用
alert、confirm、window.open—— 全部无效且阻塞流程 - 避免大体积同步操作,例如
JSON.stringify(largeData),可能拖慢冻结导致失败 - 慎用
fetch或XMLHttpRequest:在persisted === true场景下大概率被中止;改用navigator.sendBeacon()发送轻量关键数据(如用户停留时长、最后滚动位置) - 别依赖
setTimeout、Promise.then后续执行——任务队列可能已被挂起
增强可靠性:搭配 freeze 和 visibilitystate
仅靠 pagehide 不够,因为 iOS Safari 和新版 Chrome 会先触发 freeze 事件(JS 执行被暂停),再发 pagehide:
- 监听
freeze时立即执行saveState(),并设标记frozen = true - 在
pagehide中检查!frozen && !event.persisted,作为兜底保存逻辑 - 配合
document.visibilityState === 'hidden'二次确认:Safari 对pagehide触发较激进(如 App 切后台就发),加 visibility 判断可过滤误触
恢复时同步状态:用 pageshow + persisted 匹配存盘逻辑
页面从 bfcache 恢复时,pageshow 会立刻触发,且 event.persisted === true。这时应:
- 恢复冻结前的状态(如重新启用定时器、恢复动画)
- 不重置表单、不清空已输入内容——这些已在 bfcache 中原样保留
- 若需识别“真实刷新”(非 bfcache 恢复),可用
window.name跨刷新持久标识,比sessionStorage更稳定可靠











