可用 CustomEvent 定义携带 loaded、total、percent 的 download:progress 事件,通过 declare global 扩展 WindowEventMap 实现 TypeScript 类型支持,并在 fetch + ReadableStream 流式下载中实时派发,配合事件监听更新 UI 进度条。

可以用 CustomEvent 定义一个携带进度信息的自定义下载事件,核心是把进度数据(如 loaded、total、percent)作为 detail 传入,并在下载过程中多次派发该事件。
定义带进度的自定义事件类型
推荐先用 declare global 扩展 WindowEventMap,让 TypeScript 知道这个事件的存在:
declare global {
interface WindowEventMap {
'download:progress': CustomEvent<{ loaded: number; total: number; percent: number }>;
}
}这样后续使用 window.addEventListener('download:progress', ...) 时,TypeScript 就能正确推导 event.detail 的结构。
在下载逻辑中派发进度事件
以 fetch + ReadableStream 为例(适用于现代浏览器),监听流式响应并实时计算进度:
- 用
response.headers.get('content-length')获取总大小(注意:部分服务可能不返回) - 通过
response.body.getReader()逐块读取数据 - 每读取一块,累加已读字节数,计算百分比,然后调用
dispatchEvent
async function startDownload(url: string) {
const response = await fetch(url);
const total = Number(response.headers.get('content-length')) || 0;
const reader = response.body?.getReader();
let loaded = 0;
<p>if (!reader) throw new Error('ReadableStream not supported');</p><p>while (true) {
const { done, value } = await reader.read();
if (done) break;
if (value) {
loaded += value.length;
const percent = total ? Math.round((loaded / total) * 100) : 0;
window.dispatchEvent(
new CustomEvent('download:progress', {
detail: { loaded, total, percent }
})
);
}
}
}监听并更新 UI 进度条
在页面中监听该事件,将数据同步到 DOM 元素(如 <progress> 或自定义进度条):
- 确保监听器在下载开始前就注册好
- 可加防抖或节流避免高频更新(如只在
percent变化 ≥ 1% 时更新) - 处理
total === 0的情况(如流式内容、未知大小),用“加载中…”代替百分比
window.addEventListener('download:progress', (e) => {
const { loaded, total, percent } = e.detail;
const bar = document.querySelector('#progress-bar') as HTMLProgressElement;
const text = document.querySelector('#progress-text') as HTMLElement;
<p>if (bar) {
bar.value = total ? percent : 0;
bar.max = total ? 100 : 0;
}</p><p>text.textContent = total
? <code>已下载 ${loaded} / ${total} 字节 (${percent}%)</code>
: <code>正在加载... (${loaded} 字节)</code>;
});补充说明与注意事项
这个方案不依赖第三方库,但有几点需留意:
- CORS 限制:若下载跨域资源,服务端需允许
Content-Length暴露(Access-Control-Expose-Headers: Content-Length) - 兼容性:
ReadableStream在 Safari 16.4+ 和主流 Chrome/Firefox 中稳定支持;旧环境可用XMLHttpRequest的onprogress替代 - 事件命名建议用冒号分隔(如
download:progress),避免和原生事件冲突,也便于语义化管理

















