html本身不能暂停后台任务,真正响应标签页可见性变化的只有visibilitychange事件,需手动清理轮询、动画、音视频等资源,并在恢复时谨慎重启动以避免错误。

HTML 本身不能“暂停后台任务”,因为浏览器标签页切走后,页面 JS 并不会自动停掉任何逻辑——setInterval、requestAnimationFrame、WebSocket、video 都可能继续跑,消耗 CPU、内存和电量。真正能响应标签页可见性变化的,只有 visibilitychange 事件,配合手动清理与恢复逻辑。
visibilitychange 必须在 DOM 解析早期注册
多数人监听不到事件,不是 API 失效,而是脚本加晚了。Chrome 预加载、iOS Safari 恢复后台页时,document.hidden 可能已是 true,但 visibilitychange 不会“回补”已发生的状态变化。
- 把
document.addEventListener('visibilitychange', ...)写在<script></script>标签最顶部,不包裹在DOMContentLoaded或setTimeout里 - 若用构建工具(如 Vite),确保该脚本带
defer属性,且未被 code-splitting 拆走 - 不要依赖首次读取
document.hidden的值做初始化判断——它可能是true,但用户根本没切过标签页
切后台时该停什么、怎么停
浏览器不会替你关掉任何东西。重点是停那些用户看不见却持续吃资源的活儿,比如轮询、动画、音视频播放、Canvas 渲染等。
-
setInterval/setTimeout轮询 → 记下返回的 ID,document.hidden === true时调clearInterval(id) -
requestAnimationFrame动画循环 → 每次调用存 ID,hidden 时用cancelAnimationFrame(rafId);恢复时必须重新调requestAnimationFrame(loop),不能复用旧 ID -
<video></video>/<audio></audio>→ 调.pause(),但别设.currentTime = 0,保留播放进度 - Canvas/WebGL 渲染 → 停掉
render()调用,GPU 占用立刻下降 - 非关键日志上报 → 暂存到数组,
document.visibilityState === 'visible'后批量发 - 不该停:
WebSocket连接(TCP 层可能还挂着)、localStorage读写、纯计算逻辑
恢复前台时重启动要防重复和平台限制
切回来 ≠ 立刻能干活。iOS Safari 和微信 WebView 对媒体播放、定时器重启极其敏感,盲目调 play() 或 setInterval() 容易报错或卡死。
- 恢复
<video></video>前先检查video.readyState >= 4,再video.play().catch(e => console.warn('play failed:', e)) - iOS 上
video.play()必须加setTimeout(..., 2000),200ms 在部分机型上不够稳 - 轮询重启前确认
myTimer是否为null或已清除,避免setInterval多开 - 若用了
WebSocket,visible 时别急着重连——先看ws.readyState,仅当=== 0(connecting)或=== 3(closed)才触发重连逻辑 -
AudioContext在页面隐藏后会被系统 suspend,恢复时需手动调audioContext.resume(),否则音频静音
最容易被忽略的是:只清了 setInterval,却忘了 requestAnimationFrame 仍在后台疯狂执行;或者恢复时直接重用旧的动画 ID,导致丢帧甚至无限递归。真正的“暂停”不是靠浏览器自动,而是靠你对每种资源生命周期的明确控制。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











