onprogress事件仅适用于XMLHttpRequest的upload/download对象或Fetch+ReadableStream,不能直接用于img/script等标签;XHR需用xhr.download.onprogress而非xhr.onprogress,且依赖Content-Length或chunked编码;Fetch需手动读取reader并累加字节数。

onprogress 事件只能用在 XMLHttpRequest 或 Fetch 的流式响应上
直接写 onprogress 在 、<script></script> 或普通 HTML 标签上是无效的——这些标签根本不触发该事件。浏览器只对 XMLHttpRequest 的 upload 和 download 阶段,以及 fetch() + ReadableStream 解包过程提供进度反馈。
XMLHttpRequest 中正确绑定 download 进度
关键不是给 XHR 对象本身设 onprogress,而是必须监听 xhr.upload 或 xhr.download 对象的 progress 事件。漏掉 .download 就永远收不到下载进度。
-
xhr.onprogress是无效的,会被忽略 - 必须用
xhr.download.addEventListener('progress', handler)或简写xhr.download.onprogress = handler - 注意:只有当响应体实际开始传输(headers 已收完),且服务端设置了
Content-Length或使用 chunked encoding,事件才会触发 - 示例中
event.loaded是已接收字节数,event.total是总大小(若服务端没传Content-Length,total为 0)
const xhr = new XMLHttpRequest();
xhr.open('GET', '/large-file.zip');
xhr.responseType = 'blob';
xhr.download.onprogress = (e) => {
if (e.lengthComputable) {
const percent = (e.loaded / e.total) * 100;
console.log(`下载中: ${percent.toFixed(1)}%`);
}
};
xhr.send();
Fetch + ReadableStream 实现更可控的进度
Fetch 本身没有 onprogress,但可通过读取 response.body.getReader() 分块获取数据,手动累加已读字节数。这比 XHR 更灵活,也避开了 Content-Length 缺失时无法计算百分比的问题(可只显示“已加载 XXX KB”)。
- 必须显式调用
reader.read()并 await,不能跳过循环 -
response.headers.get('content-length')可提前获取总大小,但不可靠(如 gzip 压缩后不一致) - 注意:
reader.read()返回的value是Uint8Array,长度即本次读取字节数 - 不要在循环里直接更新 DOM 太频繁,建议节流或用
requestIdleCallback
const response = await fetch('/video.mp4');
const total = response.headers.get('content-length');
const reader = response.body.getReader();
let loaded = 0;
while(true) {
const { done, value } = await reader.read();
if (done) break;
loaded += value.length;
console.log(`已加载 ${loaded} / ${total || '未知'} 字节`);
}
常见失败原因:CORS、服务端配置、事件监听时机
即使代码写对,onprogress 也可能静默不触发。三个最常被忽略的点:
- CORS 响应头缺失:
Access-Control-Allow-Headers: Range和Access-Control-Expose-Headers: Content-Length必须存在,否则e.total永远为 0 - 服务端未启用流式响应:Nginx 默认缓冲整个响应再发,需配
proxy_buffering off;和chunked_transfer_encoding on; - 监听写太晚:必须在
xhr.send()之前绑定download.onprogress,否则错过初始 chunk
进度条卡在 0% 或突然跳到 100%,八成是这三个地方之一没对齐。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











