全兼容必须用显式声明多格式+严格匹配MIME类型+用户交互后JS触发播放;仅src属性或autoplay在iOS Safari等环境静默失败,因浏览器不解析文件而只按type匹配解码器。

全兼容不是选一种格式“碰运气”,而是必须用 <source></source> 显式声明多格式 + 严格匹配 MIME 类型 + 用户交互后 JS 触发播放,三者缺一不可。只写 src="a.mp3" 或只加 autoplay,在 iOS Safari、微信 WebView、旧 Android 上基本等于没写。
为什么 src 单属性写法在 Safari 里静默失败
浏览器不解析文件内容,只按 type 值匹配解码器。Safari(尤其 iOS)遇到没带 type 的 <source></source> 或整个 <audio></audio> 只有 src,直接跳过加载——不报错、canplay 不触发、duration 为 NaN、控件也不显示。
-
<audio src="a.mp3"></audio>→ iOS Safari 完全忽略 -
<source src="a.mp3"></source>(漏type)→ Firefox 可能发多个 404,Safari 直接跳过 - 正确写法必须带
type:<source src="a.mp3" type="audio/mpeg"></source>(注意不是audio/mp3) -
.ogg文件必须是 Vorbis 编码(用ffprobe a.ogg确认含Audio: vorbis),且写type="audio/ogg"
<source></source> 多格式顺序和取舍原则
浏览器从上到下尝试,第一个 canPlayType() 返回非空字符串的就停。顺序不是“越多越好”,而是按真实支持率降序排列,同时避开已知缺陷编码。
- MP3 放第一:
type="audio/mpeg",覆盖 Chrome、Safari、Edge;但避免 VBR 编码(Firefox 可能卡住) - OGG 放第二:
type="audio/ogg",仅限 Vorbis(Opus Safari 不支持),补 Firefox/Chrome 兜底 - WAV 放最后:
type="audio/wav",仅用于短提示音;别放第一位——体积大、加载慢、Chrome 拒绝 IEEE Float 编码 - OPUS 可选:
type="audio/opus",体积比 MP3 小 30%~50%,但 Safari 仍不支持,慎用
服务器响应头 Content-Type 必须严格匹配
200 状态码不等于能播。如果响应头 Content-Type 是 text/plain 或为空,浏览器当文本扔掉,连解码器都不调用——静默失败,控制台无提示。
立即学习“前端免费学习笔记(深入)”;
- Apache:在
.htaccess或虚拟主机配置中加AddType audio/mpeg .mp3、AddType audio/ogg .ogg - Nginx:在
types块里补audio/mpeg mp3;、audio/ogg ogg; - 本地开发别双击 HTML:
file://协议下 Chrome/Firefox 直接屏蔽音频加载;必须用 HTTP 服务,如python3 -m http.server 8000 - 验证方式:打开 Network 面板 → 点音频请求 → 查 Response Headers → 确认
Content-Type是audio/mpeg等合法值
JS 调用 play() 为什么总被拒绝
这不是代码 bug,是浏览器策略强制拦截。iOS Safari 和微信 WebView 要求:首次有声播放必须由真实用户手势同步触发,且元素需可见、未被 display: none 或 opacity: 0 遮挡。
- 以下写法 100% 失败:
DOMContentLoaded里调play()、setTimeout包裹、click冒泡到document - 可靠写法:绑定明确按钮,例如
document.querySelector('#playBtn').addEventListener('click', () => audio.play().catch(e => console.warn('play rejected:', e))) - 微信环境额外监听:
document.addEventListener('WeixinJSBridgeReady', () => audio.play()) - 务必加
.catch():常见错误码包括NotAllowedError(策略拦截)、NotSupportedError(格式不支持)、AbortError(网络断)
最容易被忽略的是:即使所有 HTML 和服务器都配对了,只要没走 HTTP 协议、没在真机上测过用户点击路径、或没检查 Network 面板里的 Content-Type,就大概率在某个环节静默失败——它不会报错,只会不响。



















