如何利用 Page Lifecycle API 优化网页在移动端后台被冻结时的状态保存需要先看清适用场景和关键步骤,避免只记结论却忽略实际限制。
Page Lifecycle API 的核心可用事件是 freeze 和 resume;freeze 在页面冻结前触发,可用于同步保存表单草稿、滚动位置等关键状态,此时 DOM 和 JS 仍可执行;resume 在恢复时触发,适合检查时间差、重置定时器、重新请求过期数据,但不可 reload 页面或重复绑定事件。
移动端浏览器(尤其是 Safari 和 Chrome for Android)在网页进入后台后,为节省电量和内存,会冻结页面——此时 visibilitychange 只告诉你“看不见了”,但真正能捕获冻结前最后机会的是 freeze 事件;而恢复时靠的是 resume。注意:pagehide 在部分安卓 Chrome 中可能不触发(尤其启用了 bfcache),beforeunload 在冻结场景下完全不可靠,别指望它。
实操建议:
freeze 是唯一能同步保存关键状态的时机,比如表单草稿、播放进度、滚动位置——此时 DOM 还活着,JS 仍可执行resume 里适合做轻量级恢复:检查时间差、重置定时器、重新请求过期数据,但不要在这里 reload 整页visibilitychange 来替代 freeze,因为页面可能长期 visible 但已被冻结(如分屏模式下)冻结前的 JS 执行窗口极短(通常
实操建议:
localStorage 或 sessionStorage,避免 indexedDB 写入(异步且可能来不及完成)JSON.stringify() 前先做浅拷贝并剔除函数、循环引用、DOM 节点等无法序列化的值,否则 freeze 事件处理器会直接抛错退出document.addEventListener('freeze', () => { try { const data = { scrollY: window.scrollY, draft: document.getElementById('editor')?.value?.slice(0, 1000) || '' }; localStorage.setItem('myapp-state', JSON.stringify(data)); } catch (e) { // 忽略,不能让错误阻断冻结流程 }});
resume 不代表页面“重启”,而是从冻结态唤醒——DOM 树、事件监听器、未完成的 Promise 都还在,只是时间停摆了。误判会导致双重初始化或状态错乱。
实操建议:
document.visibilityState === 'visible' 和 document.hasFocus(),确认用户确实回到了前台Date.now() - lastSavedTime 判断是否间隔太久(比如 >5min),决定是否丢弃本地缓存、重新拉取数据location.reload() 或手动触发 DOMContentLoaded 类似逻辑——页面本就没卸载resume 里重新绑定已存在的事件监听器,容易叠加导致重复执行iOS Safari 16.4+ 才支持 freeze/resume,Android Chrome 111+ 稳定支持。低于这些版本的设备根本不会触发这两个事件,必须有 fallback。
实操建议:
'onfreeze' in document 检测原生支持,不依赖 UA 字符串pagehide + visibilitychange 组合:当 visibilitychange 变为 hidden 且 document.hidden 为 true 时,立刻保存(比等 pagehide 更及时)saveAppState()),让不同事件都调它,避免逻辑分散冻结不是异常,是现代移动浏览器的正常节电行为。真正难的不是监听事件,而是在毫秒级窗口里做对事:存得精、不报错、醒得准。很多问题其实出在把 resume 当成页面重载来处理。