Page Visibility API 和页面生命周期有什么用?
简化版
Page Visibility API 可以判断页面是否可见,页面生命周期事件可以帮助在隐藏、冻结、恢复、卸载等阶段暂停或恢复任务。它常用于停止轮询、暂停视频、校准倒计时、减少后台耗电,并处理 bfcache 恢复后的状态刷新。
详细版
常见用法:
- 页面隐藏时停止无用轮询、动画和统计心跳。
- 页面显示时重新拉取关键数据。
- 视频、音频、WebSocket 等资源按业务暂停或恢复。
- 倒计时用时间戳校准,而不是依赖后台定时器。
- 页面即将隐藏时保存草稿或上报关键埋点。
注意点:
- 隐藏不等于卸载,用户可能马上回来。
- 后台定时器可能被降频或冻结。
- 从 bfcache 恢复不一定重新执行初始化。
- 不要在生命周期事件里做大量阻塞操作。
完整版教学
一、页面不是只有加载和销毁
现代浏览器页面会经历可见、隐藏、冻结、恢复、前进后退缓存等状态。用户切到别的 tab、锁屏、打开新页面、点返回,都可能改变页面状态。如果代码只理解 load 和 beforeunload,就会漏掉很多真实场景。
Page Visibility API 提供 document.visibilityState 和 visibilitychange 事件,让页面知道自己是否在前台。页面生命周期则更完整地描述页面何时暂停、恢复和退出。
visible → hidden → frozen
↑ ↓
resumed ← bfcache restore
二、隐藏时应减少无意义工作
页面不可见时,很多任务对用户没有即时价值。例如每 2 秒刷新列表、持续播放动画、频繁上报曝光。这些任务会消耗 CPU、网络和电量。浏览器也可能主动降低后台定时器频率。
数字例子:一个页面每 5 秒轮询一次接口,用户把它放后台 30 分钟,会产生 30 × 60 / 5 = 360 次请求。如果用户看不见页面,这些请求大多是浪费。隐藏时暂停,显示时刷新一次,通常更合理。
document.addEventListener('visibilitychange', () => {
if (document.visibilityState === 'hidden') {
stopPolling()
} else {
refreshData()
startPolling()
}
})
三、显示时要重新校准状态
页面从后台回到前台时,数据和时间都可能变旧。库存、消息数、订单状态、登录态可能已经变化;倒计时和定时器也可能因为后台降频而不准。恢复时应刷新关键数据,而不是默认内存状态仍然正确。
倒计时尤其要用绝对时间。不要写“每秒减 1”,而应保存结束时间,展示时用 endAt - Date.now() 计算。这样即使页面隐藏 10 分钟,回来也能显示正确剩余时间。
四、visibilitychange 不是卸载事件
页面隐藏可能只是用户切到别的应用,也可能是准备进入 bfcache,还可能随后被系统杀掉。它不等于页面一定要销毁。因此隐藏时适合做轻量保存、暂停任务和发送必要信号,不适合做复杂同步逻辑。
如果需要上报页面停留时长,可以在 hidden 时使用更可靠的轻量上报方式。不要指望 beforeunload 一定执行,移动端和 bfcache 场景下它并不可靠。
| 事件/状态 | 适合做什么 | 不适合做什么 |
|---|---|---|
| visible | 刷新数据、恢复轮询 | 重复初始化全部资源 |
| hidden | 暂停任务、保存草稿 | 大量同步计算 |
| pagehide | 收尾、记录状态 | 阻塞导航 |
| pageshow | 恢复和校准 | 假设一定首次加载 |
五、音视频和连接要按业务处理
视频页面隐藏时通常应暂停播放,恢复时可询问或按用户之前状态恢复。WebSocket 页面要看业务:聊天可能短暂保持,行情页隐藏则可以关闭或降低频率。没有一种策略适合所有页面。
关键是区分“用户暂时离开”和“业务仍需实时”。如果隐藏后还保持高频连接,就要有明确价值,否则就是电量和网络浪费。
六、和 bfcache 的关系要理解
页面进入 bfcache 时会被冻结,返回时通过 pageshow 恢复。普通初始化不一定重新执行,所以恢复逻辑要和首次加载逻辑分开。pageshow、pagehide 与 visibility 事件经常需要配合使用。
例如列表页从详情返回时,如果来自 bfcache,可以保留滚动位置,只刷新顶部未读数;如果是普通重新加载,则走完整初始化。这样既快,又不会显示过期关键数据。
记忆钩子:页面隐藏不是死了,而是“暂时离场”;回来时别忘了看时间、看数据、看连接。
七、常见误区与追问
- 误区:页面隐藏后 JS 仍会按原频率执行。 后台定时器可能被降频、暂停或冻结,不能依赖执行次数。
- 误区:visibilitychange 可以替代所有卸载逻辑。 它只是可见性变化,不保证页面销毁,也不适合重操作。
- 误区:回到前台不用刷新数据。 页面隐藏期间业务状态可能变化,应刷新关键数据。
- 追问:后台倒计时不准怎么办? 用结束时间和当前时间计算剩余,不依赖 interval 次数。
- 追问:隐藏时 WebSocket 要关闭吗? 看业务实时性,非关键场景可暂停或降频,聊天等可短暂保持。
- 追问:如何兼顾 bfcache? 用
pageshow/pagehide处理冻结恢复,用persisted判断恢复路径。
八、加强记忆
页面生命周期用“可见干活、隐藏省电、恢复校准”来记。隐藏时停掉无价值轮询和动画,恢复时刷新关键数据和时间,连接策略按业务实时性决定,再结合 pageshow/pagehide 处理 bfcache。这样页面既省资源,又不丢状态。