preload 属性对 <video> 和 <audio> 标签无效,真正有效的是 <link rel="preload" as="video/audio"> 且必须置于 <head> 中 <meta charset> 和 <title> 之后、首个阻塞资源之前。

直接说结论:preload 对 <video> 和 <audio> 标签本身无效,真正起作用的是 <link rel="preload"> 配合 as="video" 或 as="audio",且必须写在 <head> 的黄金位置——<meta charset> 和 <title> 之后、首个阻塞资源之前。
为什么 preload 属性在 <video> 上基本没用
这不是你代码写错了,是浏览器策略决定的。iOS Safari 强制忽略所有 preload 值,一律按 none 处理;Chrome 桌面版对 preload="auto" 的实际行为接近 metadata,但前提是服务端支持字节范围请求(Accept-Ranges: bytes);安卓 WebView 更是经常直接跳过。
-
preload="metadata"看似只取头信息,但若服务端没返回Content-Range或不支持Range请求,浏览器可能下载前几 KB 甚至整个文件才能解析出时长和尺寸 -
preload="none"并非“零请求”,多数浏览器仍会发一个带Range: bytes=0-1023的请求,只为拿到封面图渲染所需元数据 - 移动端页面切到后台(比如切换 tab),视频加载大概率被中止且不会恢复,依赖“提前加载完再播放”的逻辑在 iOS 上不可行
<link rel="preload"> 必须写对位置和 as 值
动态插入(比如 document.head.appendChild())完全无效——浏览器只在初始 HTML 解析阶段(parser phase)识别 preload,此时 DOM 构建还没开始。放错位置等于没写。
- 必须位于
<head>中,且紧接在<meta charset="utf-8">和<title>之后 - 必须出现在第一个
<link rel="stylesheet">或<script src>之前,否则关键资源可能已启动下载,指令被忽略 -
as值必须为"video"或"audio",写成"fetch"或漏掉,会导致降级为普通 fetch,Priority 变成 Low,Network 面板里 Initiator 不是preload - 务必验证:打开 DevTools → Network → 找对应请求 → 看
Initiator列是否为preload,Priority是否为Highest或High
如何让首帧真正快:服务端 + HTML + JS 三层协同
光靠 <link rel="preload"> 不够。MP4 文件若没有 moov box 在开头,浏览器哪怕只取 metadata 也要扫完整个文件,导致首帧延迟严重。
立即学习“前端免费学习笔记(深入)”;
- 服务端必须用
ffmpeg -c copy -movflags +faststart重写 MP4,把moov移到文件头部 - HTML 写:
<link rel="preload" href="video.mp4" as="video">(注意不是as="fetch") - JS 层不要等用户点击才调
load(),应在视口可见时(用IntersectionObserver)主动触发:video.load(),并监听canplay防止重复调用 - 避免给列表页每个
<video>都加preload或预加载——滚动时频繁创建/销毁元素容易内存泄漏,且移动端带宽宝贵
别混淆 preload 和 loading,后者对 <video> 完全无效
loading="lazy" 只对 <img> 和 <iframe> 生效,W3C 规范明确不支持 <video>。试图写 <video loading="lazy"> 不会触发任何懒加载行为,只是多了一个无意义属性。
- 想控制视频加载时机,唯一可靠方式是手动管理:初始不设
src,用data-src存地址,视口可见时再赋值并调load() - 不要在
<head>里写同步 JS 去动态创建<video>元素——这引入额外解析/执行延迟,且失去preload的早期网络调度优势 - 如果视频非首屏必需,优先考虑用 poster 图 + 点击后加载,而不是强行预加载
最容易被忽略的点不在代码怎么写,而在误判移动端加载模型:它没有“后台预加载”概念,也没有可靠的“预加载完成”状态可监听。所谓“快”,本质是服务端准备就绪 + HTML 提前声明 + JS 精准触发三者严丝合缝,缺一不可。



















