progress标签本身不触发下载,必须用JavaScript配合fetch+ReadableStream或XMLHttpRequest手动读取响应流并累计字节数更新value;服务端需返回Content-Length,否则无法计算准确百分比。

progress标签本身不触发下载,必须配合fetch或XMLHttpRequest
<progress> 是纯展示组件,没有内置网络能力。想让它动起来,得靠 JavaScript 主动读取下载过程中的响应流,并手动更新 value 属性。常见误区是直接给 <progress> 绑 download 属性或写个 a 标签就以为能联动——根本不会更新。
推荐用 fetch + ReadableStream,因为现代浏览器支持分块读取响应体,能拿到实时字节数。注意:fetch 默认不暴露 Content-Length,服务端需明确返回该 header,否则无法计算总大小,progress.max 只能设为 100 或用已知文件大小硬编码。
- 服务端响应必须带
Content-Length,否则response.headers.get('content-length')返回null - 使用
response.body.getReader()获取流读取器,每次read()返回已读字节数 - 别忘了在
<progress>上提前设好max(总字节数),否则进度条会溢出或卡在 0% - 避免直接赋值
progress.value = loaded,应做防抖或节流,高频更新可能卡 UI
如何从fetch响应流中提取已下载字节数
关键不是“怎么读”,而是“怎么累计”。ReadableStream 的 read() 返回 { value: Uint8Array, done: boolean },value.length 就是本次 chunk 的字节数。你得自己维护一个累加变量,不能只看单次 value.length。
let loaded = 0;
const contentLength = parseInt(response.headers.get('content-length') || '0');
progress.max = contentLength;
const reader = response.body.getReader();
while (true) {
const { done, value } = await reader.read();
if (done) break;
loaded += value.length;
progress.value = loaded;
}
注意:如果服务端用了 gzip 压缩,content-length 是压缩后大小,而 value.length 是解压后字节数——两者不一致,进度条会超 100%。解决办法是服务端禁用压缩,或改用 Transfer-Encoding: chunked + 自定义长度字段。
立即学习“前端免费学习笔记(深入)”;
XMLHttpRequest方案更兼容但代码略冗长
IE 和旧版 Safari 不支持 ReadableStream,这时得退回 XMLHttpRequest。它通过 onprogress 事件提供 event.loaded 和 event.total,省去手动累加,但必须设 xhr.responseType = 'blob' 才能触发进度事件(设成 '' 或 'text' 时部分浏览器不触发)。
-
xhr.onprogress在下载阶段才触发,上传阶段的upload.onprogress不相关 -
event.lengthComputable为false时说明没收到Content-Length,只能按百分比估算或显示“正在下载…”文字 - 别在
onload里才创建 blob,应在onloadend或直接用xhr.response,否则 IE 下 blob 可能为空
progress元素样式受限,自定义外观需用伪类和CSS变量
默认样式在不同浏览器差异大,Chrome 显示蓝色条,Firefox 是绿色,Safari 几乎不可见。要统一,必须覆盖 ::-webkit-progress-bar、::-webkit-progress-value、::progress-value(Firefox)等伪元素。
最稳妥的方式是用 CSS 变量驱动:progress 本身不支持 background 直接生效,但可以:
progress {
--progress-color: #4a90e2;
appearance: none;
}
progress::-webkit-progress-bar { background-color: #eee; }
progress::-webkit-progress-value { background-color: var(--progress-color); }
progress::-moz-progress-bar { background-color: var(--progress-color); }
注意:Safari 15.4+ 才支持 progress::-webkit-progress-value 的动画,旧版需用 JS 模拟过渡效果;另外,progress 无法用 transform 缩放,否则内部填充会错位。
真正麻烦的不是怎么画条,而是当用户暂停/恢复下载、断网重连、或服务端返回 206 Partial Content 时,如何同步 progress.value 和实际已缓存字节数——这部分逻辑很容易被忽略,却直接影响用户体验。



















