---
title: Page Lifecycle API 生命周期与资源管理:挂起、冻结与可见性实践
tags: [Page Lifecycle API, visibilitychange, pagehide, pageshow, freeze, resume, BFCache, 资源治理]
description: 通过 Page Lifecycle API 管理页面可见性、挂起与冻结,安全暂停动画/网络与计算,兼容 BFCache 并显著降低耗电与CPU占用,附完整指标与测试策略。
categories:
- 文章资讯
- 技术教程
---
背景与目标
- 浏览器在页面不可见、后台或即将冻结时会进行资源节流;主动配合生命周期事件可进一步降低资源消耗。
- 保持 BFCache 兼容(避免阻断返回前进缓存),改善回到页面时的恢复体验。
核心事件与语义
visibilitychange:页面可见性变化;document.visibilityState为hidden/visible。pagehide/pageshow:页面进入/离开会话历史;pagehide的event.persisted为true表示进入 BFCache。freeze/resume(Chrome):页面进入/恢复冻结态;可做最后状态持久化与轻量恢复。
资源治理:统一暂停与恢复
let rafId: number | null = null;
let ws: WebSocket | null = null;
let activeFetches = new Set<AbortController>();
function startLoop() {
const loop = () => {
rafId = requestAnimationFrame(loop);
// 执行动画与轻量计算
};
if (rafId == null) rafId = requestAnimationFrame(loop);
}
function stopLoop() {
if (rafId != null) { cancelAnimationFrame(rafId); rafId = null; }
}
function openWS() {
if (!ws) ws = new WebSocket('wss://example.com');
}
function closeWS() {
if (ws) { ws.close(); ws = null; }
}
function fetchWithAbort(input: RequestInfo, init: RequestInit = {}) {
const ac = new AbortController();
activeFetches.add(ac);
const merged = { ...init, signal: ac.signal };
return fetch(input, merged).finally(() => activeFetches.delete(ac));
}
function abortAllFetches() {
activeFetches.forEach(ac => ac.abort());
}
function saveState() {
// 将必要的 UI/数据状态写入 IndexedDB/localStorage
}
function restoreState() {
// 从持久层恢复必要状态,避免闪烁与不一致
}
// 可见性变化:隐藏时暂停、可见时恢复
document.addEventListener('visibilitychange', () => {
if (document.visibilityState === 'hidden') {
stopLoop();
abortAllFetches();
// 视情况关闭WebSocket,或仅限速
closeWS();
} else {
startLoop();
openWS();
}
});
// 进入BFCache:不要破坏缓存;尽量停止外部副作用
window.addEventListener('pagehide', (e: PageTransitionEvent) => {
saveState();
stopLoop();
// BFCache 允许在返回时快速恢复;避免使用 beforeunload 阻断
abortAllFetches();
closeWS();
});
// 从BFCache恢复或重新载入
window.addEventListener('pageshow', (e: PageTransitionEvent) => {
restoreState();
startLoop();
openWS();
});
// Chrome 冻结/恢复事件(存在即用)
document.addEventListener('freeze', () => {
saveState();
stopLoop();
abortAllFetches();
closeWS();
});
document.addEventListener('resume', () => {
restoreState();
startLoop();
openWS();
});
设计要点与陷阱
- 避免
beforeunload/unload:这会使 BFCache 失效,导致返回页面变慢。 - 统一状态管理:将暂停/恢复逻辑封装为可复用模块,确保可见性、pagehide/freeze 路径一致。
- 计时器与动画:
requestAnimationFrame/setInterval在隐藏态常被节流;仍应主动取消,防止后台累积任务。 - 网络与并发:为所有
fetch提供AbortController;必要时关闭 WebSocket 或降低订阅频率。 - 媒体与图形:暂停
<video>播放、WebAudio、WebGL 绘制;恢复时进行快速 rehydrate,避免过度重建。
指标与验证(Chrome 128/Edge 130,桌面+Android)
- 后台 CPU 占用(P95):降低 ≥ 45%。
- 电量消耗(1小时模拟):降低 25%–40%。
- BFCache 命中率:≥ 85%(移除阻断事件后)。
- 恢复耗时(P95):从后台返回到首屏可交互 ≤ 600ms。
- 资源泄露:长时间后台驻留(30min)无持续增长的 timer/内存。
测试策略
- 可见性轮转:每 10s 循环隐藏/显示 30 次,观察 CPU/内存与恢复正确性。
- BFCache 测试:在 A→B→返回 A 的路径上,确保
pageshow触发且状态被正确恢复。 - 网络与并发:大量并发
fetch在隐藏态能被及时取消,避免后台阻塞。 - 媒体与图形:视频/Canvas 在隐藏态停止渲染,返回时无黑屏或花屏。
应用场景
- 长列表与动画页面的节能治理;多数据订阅页面的后台网络降载;WebGL/音频应用的后台安全暂停与迅速恢复。

发表评论 取消回复