visibilitychange 事件需在 DOM 解析早期注册,否则冷启动或后台恢复时会错过初始状态;恢复播放须延迟并检查 readyState;有声视频必须 muted 才能 autoplay;还需统一管理 rAF、AudioContext 等资源。
visibilitychange 事件注册时机不对,监听就失效了
页面切后台时
没触发,大概率不是 API 不工作,而是监听加晚了。比如把
放在
之后、或包裹在
/
动态加载逻辑里,就可能错过初始状态——尤其是 PWA 冷启动、iOS 微信从后台恢复时,
一开始就是
,但事件不会“回补”。
必须在 DOM 解析阶段尽早注册:
直接写在
标签最顶部(不依赖任何其他脚本)
不要等
或框架的
钩子
避免用模块懒加载方式注册该监听器
只判断 document.hidden 就暂停,iOS 上 resume 失败很常见
iOS Safari 和微信 WebView 对
调用极其敏感:页面恢复后立刻调用
几乎必报错
。这不是 bug,是策略限制——即使用户刚切回来,浏览器仍视其为“无用户交互上下文”。
实操要点:
立即学习
“
前端免费学习笔记(深入)
”;
恢复播放必须加
延迟,iOS 上 2000ms 更稳妥
优先检查
(即已加载元数据且可播放),再调
捕获
的 Promise reject,失败时记录日志,不抛错阻断逻辑
示例片段:
使用HTML,CSS,JavaScript开发Android应用程序 英文文字pdf版附源文件
如果你了解HTML,CSS和JavaScript,您已经拥有所需的工具开发Android应用程序。本动手本书展示了如何使用这些开源web标准设计和建造,可适应任何Android设备的应用程序 - 无需使用Java。您将学习如何创建一个在您选择的平台的Android友好的网络应用程序,然后转换与自由PhoneGap框架到一个原生的Android应用程序。了解为什么设备无关的移动应用是未来的潮流,并开始构建应用程序,提供更
下载
没处理 audio/video 的 muted 状态,autoplay 一开始就卡死
现代浏览器(Chrome 117+、Safari 17、Firefox)默认禁止有声媒体自动播放。如果你的视频带音频轨道,又没加
属性,即使写了
,页面首次加载也会静音 + 暂停,控制台报错,后续
调用也大概率失败。
必须同时满足:
—— 四个属性缺一不可(
尤其对 iOS 必需)
如果业务必须有声,首次播放必须由用户点击等交互触发(如“点击开始”按钮),之后再用
控制续播
切后台前若已解除
,恢复时要重新设回
并重试
只停 video.pause(),漏掉 requestAnimationFrame 和 AudioContext
很多人以为暂停
就万事大吉,其实后台仍在偷偷耗电:
在 iOS Safari 中**不会自动节流**,切后台后持续执行,CPU 占用高、发热快
(Web Audio)在页面隐藏时被系统 suspend,恢复后必须显式调用
,否则音频静音
某些轮播图、粒子动画、实时图表依赖
或
,也要统一清理
正确做法是:所有周期性任务 ID(
,
,
)都存到闭包或模块级变量里,
触发时统一销毁/恢复。
容易被忽略的一点:
才能安全恢复,不能只靠
—— 因为
还可能为
或
(虽然后者极少见)。
visibilitychangeaddEventListener('visibilitychange', ...)DOMContentLoadedsetTimeoutimport()document.hiddentruewindow.onloadmountedplay()video.play()DOMException: play() failed because the user didn't interact with the document firstsetTimeout(..., 200–2000)video.readyState >= 4play()play()document.addEventListener('visibilitychange', () => {
const video = document.getElementById('myVideo');
if (document.hidden) {
video.pause();
} else {
setTimeout(() => {
if (video.readyState >= 4) {
video.play().catch(e => console.warn('resume play failed:', e));
}
}, 2000);
}
});mutedautoplayplay()playsinlinevisibilitychangemutedmuted=falseplay()requestAnimationFrameAudioContextaudioContext.resume()setIntervalsetTimeoutrafIdtimerIdaudioContextvisibilitychangedocument.visibilityState === 'visible'!document.hiddenvisibilityState'prerender''unloaded'