微信X5内核播放audio需同时满足三条件:显式声明type、用户手势触发、静音占位;仅写src无效,必须用多source按兼容性排序并匹配MIME类型,且服务端Content-Type须一致。

微信内置浏览器(X5内核)播不了 audio,不是格式问题,而是它根本没机会解码——没显式声明 type、没用户手势、没静音占位,三个条件缺一,play() 就会静默失败,连 error 事件都不触发。
必须用多 <source></source> 显式声明 type,且顺序不能错
只写 <audio src="a.mp3"></audio> 在 iOS 微信和旧版 X5 上等于没写:Safari 不解析 src 后缀,直接跳过;X5 可能加载但不触发 canplay,duration 为 NaN,控件也不显示。
-
type值必须严格匹配 MIME 类型:type="audio/mpeg"(不是audio/mp3),type="audio/ogg"(Vorbis 编码),type="audio/wav" - 顺序按兼容性降序排:
<source></source>MP3 放第一(覆盖最广),OGG 第二(Linux/部分安卓),WAV 或 OPUS 最后兜底 - 服务器响应头
Content-Type必须与type一致:Nginx 加charset utf-8; types { audio/mpeg mp3; },Apache 加AddType audio/mpeg .mp3 - 本地开发别双击 HTML 打开——
file://协议下 Chrome/Firefox 直接禁音频加载,用python3 -m http.server起服务
autoplay 必须和 muted 共存,且仍需用户手势补救
写了 autoplay muted 不代表真能响。iOS 微信和多数 X5 内核仍要求至少一次真实交互(touchstart 或 click)才能激活音频上下文。
- 必须同时写:
<audio autoplay muted preload="auto"></audio>——preload="auto"是 iOS 必需,空src或延迟赋值会导致初始化失败 - 首次播放必须在用户手势回调里触发:
document.body.addEventListener('touchstart', () => audio.play().catch(e => console.warn('play blocked:', e)), { once: true }) - 微信环境还要监听
WeixinJSBridgeReady事件,否则play()可能被忽略;不要依赖DOMContentLoaded或load事件 - 多个
<audio></audio>元素需分别预激活:对每个都执行一次play().then(() => pause()),iOS 不共享手势上下文
微信 X5 环境要绕过 UA 误判,强制走 JS 播放链路
仅靠 navigator.userAgent.includes('MicroMessenger') 判断微信不可靠:安卓微信常伪装成 MQQBrowser/6.2,部分低版本甚至不带 QQBrowser 字样。真正在 X5 上跑,得双重确认。
实现 Markdown↔ HTML 互转,支持导出 PDF,用于用户请求的文档格式转换。
立即学习“前端免费学习笔记(深入)”;
- 封装检测函数:
isWechatInX5(),需同时满足:/MicroMessenger/i.test(navigator.userAgent)且/QQBrowser\/\d+\.\d+/i.test(navigator.userAgent) || typeof window.X5JSSDK !== 'undefined' - 若为 X5,优先使用微信 JS-SDK 的
wx.ready触发播放:wx.config({ debug: false, jsApiList: [] })后,在wx.ready回调中调用audio.play() - 避免直接引用
http://res.wx.qq.com/open/js/jweixin-1.0.0.js—— iOS 可能因 CSP 或缓存报ReferenceError: wx is not defined;建议复制 SDK 内容到本地 JS,或用 HTTPS + 安全域名接入 - 不要用
volume = 0替代muted:浏览器不认,无法绕过策略
音频加载失败时必须捕获 Promise reject,不能只监听 error 事件
audio.play() 返回 Promise,但失败原因五花八门:NotAllowedError(策略拦截)、NotSupportedError(格式不支持)、AbortError(网络中断)。不 .catch(),你就永远不知道卡在哪。
- 必须写:
audio.play().catch(e => { if (e.name === 'NotAllowedError') { /* 引导用户点击按钮 */ } else if (e.name === 'NotSupportedError') { /* 提示换格式或设备 */ } }) -
error事件只在资源加载失败时触发(如 404),对策略拦截无效;stalled和suspend事件也需监听,判断是否卡在缓冲 - 测试务必用真机 + 文件传输助手发本地 HTML —— 微信开发者工具模拟不了真实 X5 渲染链路和音频策略
最容易被忽略的是:X5 内核对音频上下文的激活极其“短视”——用户点一下,只激活当前 <audio></audio>,后续新增的音频元素不会继承权限;而且一旦页面 visibility 变为 hidden,上下文大概率失效,切回来得重新触发手势。这不是 bug,是它的设计逻辑。


















