video.buffered 是获取视频缓冲进度的唯一可靠数据源,需结合 timeupdate 或轮询(推荐300ms)读取,且须在 readystate≥have_future_data 后访问;计算百分比时须处理 currenttime 超出缓冲区、duration 为 infinity、buffered.length 为 0 三类边界;ui 显示需用滑动窗口平滑数值,避免抖动。

video.buffered 是唯一可靠的数据源,但不能直接监听它
浏览器没有 bufferedupdate 这个标准事件 —— Chrome 曾短暂支持过,2023 年起已移除。你绑了也收不到任何触发,别白费劲。
真正能用的只有 timeupdate(播放时自动高频触发)或主动轮询(setInterval)。尤其当视频暂停、静音、或刚拖动完,timeupdate 可能停发,但缓冲仍在后台进行,这时轮询反而是更稳的选择:
- 推荐每
300ms检查一次video.buffered,比timeupdate更可控 - 必须等
video.readyState >= HTMLMediaElement.HAVE_FUTURE_DATA才开始读buffered,否则会拿到空区间或NaN - 首次加载时,
loadedmetadata或canplay事件后才可安全访问buffered.length
getBufferedPercent() 函数必须处理三个边界情况
直接拿 video.buffered.end(0) / video.duration * 100 算百分比,线上一跑就出错。真实场景中至少要防三类坑:
- 当前播放点
video.currentTime超出所有已缓冲区间 → 返回0(不是负数,也不是抛错) -
video.duration === Infinity(如直播流)→ 百分比无意义,应改算“还能往回拖多少秒”,用video.buffered.end(i) - video.currentTime -
video.buffered.length === 0→ 直接返回0,别试图取end(0),会报IndexSizeError
参考实现逻辑:
function getBufferedPercent(video) {<br> const time = video.currentTime;<br> let bufferedEnd = 0;<br> for (let i = 0; i < video.buffered.length; i++) {<br> if (video.buffered.start(i) <= time && time <= video.buffered.end(i)) {<br> bufferedEnd = video.buffered.end(i);<br> break;<br> }<br> }<br> return video.duration ? Math.min(100, (bufferedEnd / video.duration) * 100) : 0;<br>}
立即学习“前端免费学习笔记(深入)”;
UI 显示缓冲进度必须做平滑,否则肉眼可见抖动
网络波动会让 buffered 区间频繁增减,同一秒内可能从 45% → 38% → 52%,直接塞进进度条 UI,用户会觉得“卡顿”或“bug”。这不是计算错了,是没做信号滤波。
最轻量的做法是维护一个长度为 3 的滑动窗口,每次新值进来就更新平均值:
- 存三个最近的
getBufferedPercent()结果,每次取Math.round((a + b + c) / 3) - 不建议用
setTimeout延迟更新 —— 拖动时响应会滞后,用户感知明显 - 如果缓冲长期停滞(比如连续 5 秒没变化),可额外加个 loading 动效提示,而不是只靠数字
缓存策略和缓冲行为是两回事,别混为一谈
有人以为在 HTML 里写 <meta http-equiv="Cache-Control" content="max-age=3600"> 就能控制视频缓冲,其实完全无效。浏览器根本不认这个 meta 标签里的 Cache-Control —— 它只看 HTTP 响应头。
真正影响缓冲的是服务端配置:
- MP4 文件需由 Nginx/Apache 返回带
Accept-Ranges: bytes的响应,否则无法拖动、也无法分段缓冲 - 直播流(如 HLS/FLV)依赖服务器支持分片与时间戳对齐,前端再怎么监听
buffered也救不了后端丢帧 -
preload="auto"只是建议,浏览器可能无视;而preload="none"会彻底禁掉预缓冲,哪怕用户点播放也不会自动加载
缓冲百分比只是表象,背后是网络、CDN、MSE 分段、服务端 range 支持的综合结果。盯着 video.buffered 看,不如先抓包确认第一块 206 Partial Content 是不是按时回来了。











