backgroundfetch.onprogress只能在service worker中监听,不能在页面主线程绑定;需在任务创建后、sw激活状态下通过registration.backgroundfetch.fetch()发起,并用clients.matchall()配合postmessage将进度推送给页面。

backgroundFetch.onprogress 只能在 Service Worker 里监听
你无法在页面主线程中直接绑定 backgroundFetch.onprogress——这个事件监听器只能注册在 Service Worker 的作用域内,且必须在任务创建后、Service Worker 处于 active 状态时设置。常见错误是把监听写在页面 JS 里,结果完全没反应。
正确做法是:在 Service Worker 脚本中调用 registration.backgroundFetch.fetch() 创建任务后,立即用 backgroundFetch.onprogress 监听其进度变化。注意该 API 返回的是 Promise,但进度事件本身是异步触发的,不阻塞主流程。
- 必须在
self.addEventListener('install', ...)或'activate'后确保 Service Worker 已 ready,否则registration.backgroundFetch可能为undefined -
onprogress回调参数是一个对象,含id(任务 ID)、uploaded(已上传字节数)、downloaded(已下载字节数)、total(服务端声明的总大小,来自Content-Length或downloadTotal配置) - 它不是高频推送:浏览器按策略节流(例如每秒最多触发几次),不会像
XMLHttpRequest.upload.onprogress那样每 KB 就触发一次
progress 里的 uploaded/downloaded 不等于文件 size,别硬除
uploaded 和 downloaded 是浏览器内部计数,受网络栈、缓存、重试逻辑影响,可能略大于或小于原始文件大小;而 total 若未显式配置 downloadTotal,则依赖响应头 Content-Length——如果服务端用了 Transfer-Encoding: chunked 或未暴露该 header,total 就是 null,此时无法算百分比。
所以别写 Math.round(downloaded / file.size * 100) 这类代码,容易 NaN 或溢出。稳妥方式是:
- 下载任务:优先用创建时传入的
downloadTotal值做分母(你控制,最可靠) - 上传任务:Background Fetch 目前不暴露上传总大小,
uploaded单独存在但无total字段,实际无法算百分比——这是当前 API 的明确限制 - 若
total === null,只显示“已接收downloaded字节”,别强行补 100%
主线程怎么拿到 progress 数据?靠 postMessage 中转
Service Worker 里监听到 onprogress 后,得主动把数据发给页面,不能指望页面自己去查。用 clients.matchAll() 找到对应页面 client,再用 client.postMessage() 推送结构化消息。
示例关键片段(Service Worker 内):
backgroundFetch.onprogress = async (event) => {
const { id, downloaded, total } = event;
const clients = await self.clients.matchAll({ type: 'window' });
clients.forEach(client => {
client.postMessage({
type: 'bgfetch-progress',
id,
downloaded,
total,
percent: total ? Math.round((downloaded / total) * 100) : null
});
});
};
页面 JS 中监听:navigator.serviceWorker.addEventListener('message', ...),再更新 <progress></progress> 的 value。注意要校验 event.data.type === 'bgfetch-progress',避免混淆其他消息。
- 不要在每次
postMessage后立刻更新 DOM——高频推送下易卡 UI,建议加requestAnimationFrame节流 - 如果用户关闭了页面,
clients.matchAll()可能返回空数组,此时消息就丢了,属于正常行为 - 别在 Service Worker 里操作 DOM,所有 UI 更新必须交由页面线程完成
为什么 backgroundFetch.onprogress 没触发?先看这三处
最常见的静默失败不是代码写错,而是环境或配置断链:
- 浏览器不支持:Chrome ≥ 85、Edge ≥ 85 支持,Firefox 和 Safari 完全不支持(截至 2026 年 5 月)。用
'backgroundFetch' in registration检测,别只查navigator.serviceWorker - Service Worker 被终止:后台任务运行中,系统可能因内存压力 kill SW。用
extendableEvent.waitUntil()包裹 fetch 调用可延长生命周期,但不能无限保活 - 任务 ID 冲突或重复注册:同一个
id多次调用fetch()会覆盖旧任务,旧的onprogress监听自动失效。调试时建议用时间戳 + 随机数生成唯一id
真正难搞的是进度“看起来卡住”:比如 downloaded 长时间不变。这不是 bug,是浏览器对后台传输的保守调度策略——它可能合并多个小包、延迟上报,尤其在弱网或低电量模式下。别把它当异常处理,更别自动重试。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











