现代浏览器禁止音频自动播放,必须由用户交互(如click)同步触发play();ios safari最严格,需playsinline、用户已点击且在事件回调首层调用;静音播放需muted="true"且volume=0;务必捕获play()异常并显式声明source的type属性。

现代浏览器基本不让你的 <audio></audio> 自动播放有声内容,这不是代码写错了,而是策略强制限制——你得按浏览器的规则来,否则 play() 必然抛 DOMException: play() failed because the user didn't interact with the document first。
audio.play() 被拒绝的真正原因
不是“没加 autoplay 属性”,而是浏览器要求:必须有用户真实交互(click/touch/keydown)触发、且该触发必须发生在调用 play() 的同步上下文中。Chrome、Firefox、Safari 全部执行此策略,只是细节略有差异:
- iOS Safari 最严:即使加了
muted,也要求页面此前已获得过用户手势授权(比如用户点过任意地方),否则静音也不播 - Chrome 允许静音自动播,但前提是
muted属性存在(不能写成muted="false")、volume保持为 0、且未被 JS 后续改写 - 所有浏览器都会在页面失焦一段时间后重置
navigator.userActivation.hasBeenActive,导致后续定时器里的play()失败
必须用用户交互触发播放(最可靠方案)
放弃 onload 或定时器自动调用 play(),改用一次性的用户事件监听。重点不是“播不播”,而是“谁触发、怎么触发”:
- 监听
document.addEventListener('click', handler, { once: true })或touchstart(移动端优先) -
handler内必须同步调用audio.play(),不能包在setTimeout、Promise.then或异步函数里 - 若想先静音再开声,要在
play()成功 resolve 后再设audio.muted = false,否则部分浏览器会中断播放 - 务必用
.catch()捕获失败,避免阻塞后续逻辑:audio.play().catch(e => console.warn('play rejected:', e))
多格式 fallback 和 MIME 类型必须显式声明
单靠 src 属性在 Safari、旧 Android WebView 上大概率静默失败,浏览器根本不会尝试加载——它只认 type 值是否匹配内置解码器:
- 每个
<source></source>标签都必须带type,MP3 写type="audio/mpeg",不是audio/mp3 - 顺序很重要:把兼容性最强的放最前,例如
<source src="a.mp3" type="audio/mpeg"></source>→<source src="a.ogg" type="audio/ogg"></source> - 本地开发时别用
file://协议跑测试,Chrome 直接禁用play();改用python3 -m http.server或其他本地服务器 - Network 面板里确认音频请求返回 200,且响应头
Content-Type是audio/mpeg等合法类型,否则就算路径对也播不了
iOS Safari 的特殊硬性要求
它不接受任何“绕过”逻辑,只认三件事:用户点过、playsinline 属性存在、play() 在事件回调第一层执行:
- 必须加
playsinline属性,否则可能跳转到系统音乐 App 播放 -
preload设为"metadata","auto"在 iOS 上基本无效且耗流量 - 不要在
canplay或loadeddata回调里调play(),iOS 不认为这是用户手势上下文 - 避免
loop+autoplay组合,容易卡住或反复报错;循环逻辑建议用ended事件手动控制
最容易被忽略的是:用户首次交互之后的“激活窗口”只有约 5 秒,超时再调 play() 就又得等下一次点击;而页面失焦后这个状态直接归零——所以做通知音效时,不能只依赖一次初始化,得持续监听 visibilitychange 并结合 navigator.userActivation.hasBeenActive 动态判断。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











