preload="metadata"是最稳妥的默认值,仅请求moov box获取元信息而不下载音画数据,但需服务端支持Accept-Ranges: bytes且视频经ffmpeg faststart重写;preload="none"需JS显式load()配合IntersectionObserver;preload="auto"基本无效,iOS Safari始终降级为none,autoplay会忽略preload;动态创建的video元素preload不生效;服务端配置比HTML写法更关键。

preload="metadata" 是最稳妥的默认值
它只请求视频文件开头的 moov box(通常几 KB),用来获取时长、宽高、封面帧、音轨数等元信息,不下载任何画面或音频数据。几乎所有现代浏览器(Chrome、Firefox、Safari 桌面版、主流 Android WebView)都严格遵守这一行为。
但必须满足两个硬前提:
- 服务端响应头需包含
Accept-Ranges: bytes,否则浏览器无法发字节范围请求,可能退化为下载前几 MB 甚至整个文件 - 视频文件需用
ffmpeg -c copy -movflags +faststart重写,确保moov在文件开头;否则浏览器得下完才能解析元数据
务必搭配 poster 属性显示占位图,并监听 loadedmetadata 事件确认就绪——别依赖 video.readyState 初始值,它在 iOS Safari 上长期为 0。
preload="none" 要配合 JS 显式调用 load()
设成 none 后,仅赋值 src 不会触发任何网络请求,连 DNS 查询都跳过。这在流量敏感场景(如微信内嵌 WebView、蜂窝网络)很有效,但代价是:你必须自己控制加载时机。
立即学习“前端免费学习笔记(深入)”;
典型做法是结合 IntersectionObserver:
- 当视频即将进入视口时,先设置
video.preload = "metadata" - 再立即调用
video.load() - 监听
canplay或loadeddata后才展示控件或允许播放
注意:微信 X5 内核对首次 load() 有静默丢弃风险,可加 setTimeout(() => video.load(), 0) 微调;完全移出可视区后,建议设 video.src = "" 释放已加载资源(会中断当前请求)。
preload="auto" 在多数环境里基本无效
名字带 “auto”,实际几乎从不自动全量加载。iOS Safari 从 10 开始就无视所有 preload 值,静默降级为 none;Chrome 桌面版通常只预加载前 1–5 秒;Android WebView 和微信内核也普遍降级为 metadata。
更关键的是:只要写了 autoplay,preload 就被完全忽略——这是规范行为,不是 bug。动态 JS 设置 video.preload = "auto" 也无效,因为 preload 只在 HTML 解析阶段起作用。
若真需要激进预加载(如课程主视频),应改用:<link rel="preload" href="xxx.mp4" as="video" type="video/mp4">
它主动发起请求(仅同域),但不触发解码,也不影响 video 元素自身状态。
动态插入的 video 元素 preload 完全不生效
React/Vue 渲染的组件、或 JS 用 document.createElement('video') 创建的元素,即使模板里写了 preload="metadata",也不会触发预加载——它没参与初始 HTML 解析流程。
真正可控的做法只有两种:
- 首屏核心视频:用静态 HTML +
preload="metadata"+poster - 滚动列表中的视频:靠
IntersectionObserver监听进入视口后,显式调用video.load()
最容易被忽略的一点是:服务端配置比 HTML 写法更重要。没有 Accept-Ranges: bytes,preload="metadata" 就可能变成流量黑洞;moov 不在开头,首帧延迟就不可控——这些都不在 HTML 里写,但决定了你配得再对也没用。



















