---

title: Page Lifecycle API 生命周期与资源管理:挂起、冻结与可见性实践

tags: [Page Lifecycle API, visibilitychange, pagehide, pageshow, freeze, resume, BFCache, 资源治理]

description: 通过 Page Lifecycle API 管理页面可见性、挂起与冻结,安全暂停动画/网络与计算,兼容 BFCache 并显著降低耗电与CPU占用,附完整指标与测试策略。

categories:

  • 文章资讯
  • 技术教程

---

背景与目标

  • 浏览器在页面不可见、后台或即将冻结时会进行资源节流;主动配合生命周期事件可进一步降低资源消耗。
  • 保持 BFCache 兼容(避免阻断返回前进缓存),改善回到页面时的恢复体验。

核心事件与语义

  • visibilitychange:页面可见性变化;document.visibilityStatehidden/visible
  • pagehide/pageshow:页面进入/离开会话历史;pagehideevent.persistedtrue 表示进入 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/音频应用的后台安全暂停与迅速恢复。

点赞(0) 打赏

评论列表 共有 0 条评论

暂无评论
立即
投稿

微信公众账号

微信扫一扫加关注

发表
评论
返回
顶部