如何利用Page Lifecycle API异步抓取页面回收瞬间,实现用户输入数据的恢复?
- 内容介绍
- 相关推荐
本文共计898个文字,预计阅读时间需要4分钟。
Page Lifecycle API 无法异步捕获 '系统回收瞬间',因为 discarded 状态本身不可监听——浏览器在进入该状态时已完全终止 JavaScript 执行环境,所有事件、定时器、fetch 请求、乃至 beforeunload 事件都不会触发。所谓‘捕获回收’,实质上是利用冻结前的可靠时机保存+加载时的多信号交叉判断,实现对用户输入的精确还原。
真正可用的保存时机只有 freeze 和 pagehide(persisted=true)
这两个事件是 JS 能安全执行并持久化数据的最后窗口:
- freeze 事件:页面即将进入 Frozen 阶段,DOM 可读、JS 可写,是保存表单草稿、滚动位置、选中状态等关键输入的首选时机
- pagehide 且 event.persisted === true:说明页面将被放入 bfcache 或 Frozen,后续可能直接 resume,也应同步保存(作为 freeze 的兜底)
- 避免依赖
visibilitychange或blur:它们只反映可见性或焦点变化,与系统是否冻结/丢弃无直接关系,中间存在不可控空窗期
保存内容要轻量、可序列化、带时间戳
冻结阶段不允许发起新请求、不能操作 DOM、不支持函数或复杂对象。推荐做法:
- 仅存用户可见的关键输入:如
textarea.value、input.value、select.selectedIndex、window.scrollY - 用
sessionStorage存储,比 localStorage 更贴合单页会话生命周期(关闭 Tab 后自动清理) - 写入时附带时间戳:
{ draft: "...", savedAt: Date.now() },便于后续判断是否过期 - 敏感字段(如密码、token)不建议本地存储,恢复时应走服务端校验
加载时靠多信号组合判断是否为 discard 冷启动
用户切回 Tab 后,若页面已被 discard,浏览器会全新加载。此时需结合以下信号交叉验证:
-
pageshow事件中event.persisted === false(基础信号,但普通刷新也满足) - 检查 URL 是否含标记参数,如
?_reloaded=1(需你在上一次 freeze/pagehide 中主动写入并跳转) - 读取 sessionStorage 中的时间戳,若距当前 > 5 分钟,大概率已 discard(Frozen 通常仅维持 2–3 分钟)
-
performance.getEntriesByType('navigation')[0]?.type为'navigate'或'reload',且无其他上下文线索
还原逻辑必须区分冷启动与热恢复
不能一概而论地“恢复输入”,不同场景策略不同:
-
冷启动(discard 后重建):DOM 全新渲染,
document.hasFocus() === true,无残留状态 → 从 sessionStorage 拉取快照,手动还原表单值、滚动位置、tab 选中等 -
热恢复(Frozen → Active 或 bfcache resume):DOM 和 JS 状态基本保留,
pageshow.persisted === true或resume事件触发 → 不重填表单,只检查并重连网络请求、重置失效定时器、更新倒计时等 UI 状态 - 还原后建议清除旧快照,避免下次误用;可加节流(如 200ms 内不重复还原)防抖
本文共计898个文字,预计阅读时间需要4分钟。
Page Lifecycle API 无法异步捕获 '系统回收瞬间',因为 discarded 状态本身不可监听——浏览器在进入该状态时已完全终止 JavaScript 执行环境,所有事件、定时器、fetch 请求、乃至 beforeunload 事件都不会触发。所谓‘捕获回收’,实质上是利用冻结前的可靠时机保存+加载时的多信号交叉判断,实现对用户输入的精确还原。
真正可用的保存时机只有 freeze 和 pagehide(persisted=true)
这两个事件是 JS 能安全执行并持久化数据的最后窗口:
- freeze 事件:页面即将进入 Frozen 阶段,DOM 可读、JS 可写,是保存表单草稿、滚动位置、选中状态等关键输入的首选时机
- pagehide 且 event.persisted === true:说明页面将被放入 bfcache 或 Frozen,后续可能直接 resume,也应同步保存(作为 freeze 的兜底)
- 避免依赖
visibilitychange或blur:它们只反映可见性或焦点变化,与系统是否冻结/丢弃无直接关系,中间存在不可控空窗期
保存内容要轻量、可序列化、带时间戳
冻结阶段不允许发起新请求、不能操作 DOM、不支持函数或复杂对象。推荐做法:
- 仅存用户可见的关键输入:如
textarea.value、input.value、select.selectedIndex、window.scrollY - 用
sessionStorage存储,比 localStorage 更贴合单页会话生命周期(关闭 Tab 后自动清理) - 写入时附带时间戳:
{ draft: "...", savedAt: Date.now() },便于后续判断是否过期 - 敏感字段(如密码、token)不建议本地存储,恢复时应走服务端校验
加载时靠多信号组合判断是否为 discard 冷启动
用户切回 Tab 后,若页面已被 discard,浏览器会全新加载。此时需结合以下信号交叉验证:
-
pageshow事件中event.persisted === false(基础信号,但普通刷新也满足) - 检查 URL 是否含标记参数,如
?_reloaded=1(需你在上一次 freeze/pagehide 中主动写入并跳转) - 读取 sessionStorage 中的时间戳,若距当前 > 5 分钟,大概率已 discard(Frozen 通常仅维持 2–3 分钟)
-
performance.getEntriesByType('navigation')[0]?.type为'navigate'或'reload',且无其他上下文线索
还原逻辑必须区分冷启动与热恢复
不能一概而论地“恢复输入”,不同场景策略不同:
-
冷启动(discard 后重建):DOM 全新渲染,
document.hasFocus() === true,无残留状态 → 从 sessionStorage 拉取快照,手动还原表单值、滚动位置、tab 选中等 -
热恢复(Frozen → Active 或 bfcache resume):DOM 和 JS 状态基本保留,
pageshow.persisted === true或resume事件触发 → 不重填表单,只检查并重连网络请求、重置失效定时器、更新倒计时等 UI 状态 - 还原后建议清除旧快照,避免下次误用;可加节流(如 200ms 内不重复还原)防抖

