无法捕捉“回收瞬间”,因discarded状态不可监听;“自愈”依赖提前保存+事后推断+分场景还原:freeze事件是唯一同步保存时机,pagehide(persisted=true)为必要补充,需多维线索组合判断页面状态。

无法捕捉“回收瞬间”,因为 discarded 状态本身不可监听;所谓“自愈”本质是提前保存 + 事后推断 + 分场景还原。
freeze 是唯一能同步保存用户交互数据的时机
页面被冻结前,浏览器会触发 freeze 事件,此时 JS 仍可执行、DOM 可读、localStorage.setItem() 能成功写入——这是最后且最可靠的保存窗口。错过它,就等于放弃所有现场。
- 只读取可序列化字段:用
input.value、textarea.value、window.scrollY,别存document.querySelector()或函数引用 - 加防抖:用
performance.now()记录上次保存时间,200ms 内不重复触发,避免和pagehide冲突 - 不依赖异步:不要
await localStorage.setItem(),它可能被中断;改用同步调用或降级到sessionStorage - 标记状态:设全局
frozen = true,方便后续逻辑判断是否已保存
pagehide(persisted=true) 是必须补上的保险
部分 Safari 和旧版 Android Chrome 不触发 freeze,而是直接走 pagehide 并把 event.persisted 设为 true。如果你只监听 freeze,这部分用户的数据就彻底丢了。
- 在
pagehide回调里检查event.persisted === true && frozen !== true,满足才执行保存 - 避免在
pagehide中读取 DOM 布局信息(如getBoundingClientRect()),此时元素可能已不可见,返回值为 0 - 别信
beforeunload:它在冻结/丢弃场景下完全不触发,Chrome 已明确将其标记为非标准行为
pageshow.persisted === false 不代表 discard,只是冷启动信号
pageshow 事件中 event.persisted === false 表示这次不是从 bfcache 恢复,但可能是刷新、导航或 discard 后重建——仅凭它无法区分。必须交叉验证:
- 检查 URL 是否含
?_reloaded=1:需你在freeze或pagehide里主动重定向或写入localStorage并跳转 - 读
localStorage中的时间戳:若距Date.now()> 5 分钟,大概率已被 discard(Frozen 通常只维持 2–3 分钟) - 查
performance.getEntriesByType('navigation')[0]?.type:值为'reload'或'navigate'时,倾向 discard 后重建 - 注意
document.hasFocus() === true且无残留 DOM 状态(如表单没值、滚动位置为 0),才是冷启动典型表现
resume 事件不是初始化入口,误用会导致状态错乱
resume 触发时,页面并未重新加载:DOM 树、未完成的 Promise、定时器 ID、事件监听器都原样保留。把它当“新页面”处理,极易引发重复绑定、双重请求、UI 覆盖。
- 只做轻量校验:比如检查距上次保存是否超时、重置已失效的
setTimeout、拉取过期数据 - 不调用
location.reload(),不重建 React/Vue 根实例,不重复绑定全局事件 - 恢复 UI 时优先用保存的原始值,而非实时读取 DOM(如用
savedScrollY而非document.documentElement.scrollTop) - 敏感操作(如 token 刷新)应在服务端校验,
resume中只做静默重试,不暴露本地存储的凭证
真正难的不是写保存逻辑,而是意识到:你永远不知道用户切回来时面对的是 Frozen resume、bfcache resume,还是 discard 后的全新页面——三者 DOM 行为一致,但 JS 状态天差地别。靠单一信号判断必出错,必须用多维线索组合建模,再分路径处理。
大量免费API接口:立即使用
涵盖生活服务API、金融科技API、企业工商API、等相关的API接口服务。免费API接口可安全、合规地连接上下游,为数据API应用能力赋能!










