mp4必须置于首位以确保ios/safari兼容性;webm/ogg等应后置或按需加载;私密模式下需用事件二次验证而非仅依赖canplaytype()。

<source></source> 标签的 fallback 顺序不是随便写的,浏览器按 DOM 中出现顺序逐个试探,遇到第一个 canPlayType() 返回非空字符串("probably" 或 "maybe")的就停,后面全跳过。写错顺序,用户可能根本播不出视频。
为什么 MP4 必须放 <source></source> 第一位
不是因为“MP4 最好”,而是因为兼容性事实:iOS/iPadOS 全系只认 H.264/AAC 编码的 MP4;Safari 对 WebM 返回空字符串(哪怕你写了 type="video/webm");Chrome 和 Firefox 虽支持 WebM,但若 MP4 已被选中,它们根本不会走到第二条 <source></source>。所以 MP4 是保底项,必须前置。
- 写成
<source src="a.mp4" type="video/mp4"></source>,不是type="video/mp4; codecs="avc1.42E01E""—— 后者只要引号、大小写、空格或编码器名稍有偏差,整条就被忽略 - 不要把
OGG/Theora放在 MP4 前面,它在现代浏览器里基本没人用,还可能干扰 Safari 的解析逻辑 - 如果服务端已确认用户来自 iOS 15+,可省略 WebM,减少 HTML 体积和加载开销
canPlayType() 返回空字符串 ≠ 不支持,尤其在 Safari 私密模式下
Safari 在无痕/私密窗口中会主动对部分格式返回空字符串,这是故意行为,不是 bug。此时仅靠 canPlayType() 判断会误判为“全都不支持”,导致降级到错误路径(比如提前显示 GIF 或报错)。
- 必须配合
loadedmetadata或stalled事件做二次验证:即使canPlayType()返回空,仍让浏览器尝试加载第一条<source></source>,监听是否触发onstalled - 若
video.readyState === 0且video.networkState === NETWORK_NO_SOURCE,才确认真正无可用源 - 不要在
canPlayType()返回空时直接移除所有<source></source>—— 这会让 Safari 私密模式彻底失去播放能力
WebM 源要不要加 codecs 参数
加了反而容易失效。VP9 和 Opus 的 codec 字符串写法极敏感:codecs="vp09.00.10.08" 和 codecs="vp09.00.10.08.01" 就算只差一个点,Safari 或旧版 Chrome 可能直接返回空字符串。
- 推荐只写
type="video/webm",不带codecs—— 浏览器自己决定能否解码,更可靠 - 若必须指定 VP9,用
canPlayType('video/webm; codecs="vp09"')探测,返回"probably"再动态插入带codecs的<source></source>,避免静态 HTML 里硬编码出错 - AV1 源同理,
canPlayType('video/webm; codecs="av01.0.05M.08"')返回空时,别让它参与 fallback 链路,否则阻塞后续 MP4 加载
最易被忽略的一点:多个 <source></source> 共享同一个 video 元素,但它们的 src 一旦被浏览器开始加载,就无法被“撤回”。所以 fallback 顺序必须在 HTML 渲染前就定死,不能靠 JS 动态重排 —— 那样已经触发的请求不会取消,只会造成冗余请求和卡顿。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











