Background Fetch API 在 Chrome/Edge 105+ 可用,Firefox/Safari 不支持,需 HTTPS/localhost、显式调用 fetch() 且确保 navigator.backgroundFetch 存在和 serviceWorker.ready 已就绪。

Background Fetch API 是否可用,先看浏览器和注册状态
Background Fetch 在 Chrome 105+、Edge 105+ 中可用,Firefox 和 Safari 完全不支持,且必须在 HTTPS 或 localhost 下运行。它不是“只要注册 Service Worker 就自动生效”的功能——你得显式调用 backgroundFetch.fetch(),而且前提是 navigator.backgroundFetch 存在且 navigator.serviceWorker.ready 已 resolve。
常见错误现象:TypeError: navigator.backgroundFetch is undefined 或注册后调用 fetch() 报 InvalidStateError。
- 检查前务必确认
if ('backgroundFetch' in navigator),否则直接跳过逻辑或降级到StreamSaver.js+ Blob URL 方案 - Service Worker 脚本必须注册在根路径(如
/sw.js),否则backgroundFetch权限可能受限 - 注册后不要立即调用
fetch(),需等navigator.serviceWorker.readypromise settle,否则会因 SW 尚未激活而失败
如何发起一个带元数据的后台下载任务
backgroundFetch.fetch() 不是简单封装 fetch(),它要求传入 id、requests(数组)、options,且 requests 中每个元素必须是标准 Request 实例(不能是字符串 URL)。
容易踩的坑:直接传 ['/large.zip'] 会报错;漏掉 title 导致通知栏无标题;没设 icons 在 Android 上无法显示自定义图标。
立即学习“前端免费学习笔记(深入)”;
-
requests必须用new Request(url, { method: 'GET' })构造,headers 若含认证需显式携带(SW 里无法读取页面 cookie) -
options中title是必填字段,icons推荐提供至少一个 192x192 PNG,否则用默认图标 - 同一个
id重复调用会覆盖前一个任务,如需并发,id 必须唯一(例如拼上时间戳或 UUID)
const registration = await navigator.serviceWorker.ready;
const bgFetch = await registration.backgroundFetch.fetch(
'download-123',
[new Request('https://example.com/file.zip')],
{
title: '正在下载大文件',
icons: [{ src: '/icon-192.png', sizes: '192x192', type: 'image/png' }],
downloadTotal: 1024 * 1024 * 100 // 可选,用于进度估算
}
);如何监听进度与处理完成事件
进度不是实时推送的——backgroundFetch.onprogress 是 Service Worker 内部监听器,只能在 SW 脚本里注册;页面脚本无法直接监听,必须靠 postMessage 主动通信。
常见错误现象:页面里写 navigator.backgroundFetch.addEventListener('progress', ...) 完全无效;任务完成但 UI 没更新,因为没处理 backgroundfetchsuccess 事件。
- 在 SW 中监听
backgroundfetchsuccess和backgroundfetchfail,再用clients.matchAll()找到页面 client 并postMessage -
onprogress的回调参数是{ developerId, progress: { totalBytes, transferredBytes } },注意totalBytes可能为 0(服务端未返回 Content-Length) - 任务完成后,
registration.backgroundFetch.get(id)返回的BackgroundFetchRegistration对象含result('success'或'failure')和failedRecords数组
为什么下载完文件却打不开?注意响应体提取限制
Background Fetch 下载的文件不会自动存到用户磁盘,也不会生成可直接访问的 URL。它只把响应体存在内部存储中,你必须在 backgroundfetchsuccess 事件里调用 registration.matchAll() 获取匹配的 Response,再用 response.blob() 提取内容——但此时已脱离原始请求上下文,无法还原 filename 或 Content-Disposition。
关键限制:SW 里无法触发浏览器原生下载(location.href=URL.createObjectURL(blob) 在 SW 中无效),必须把 blob 发回页面处理。
- 页面收到 blob 后,要用
URL.createObjectURL()创建临时 URL,再创建a标签触发下载;注意 blob 默认名是download,需手动设download属性 - 若服务端返回了
Content-Disposition: attachment; filename="report.pdf",这个信息在 SW 中不可读,必须由前端提前约定文件名或从 URL 解析 - 大文件(>500MB)提取 blob 时可能触发内存压力,Chrome 会终止 SW,建议分块下载或改用
Streamsaver.js流式写入
真正难的不是发起任务,而是让整个链路闭环:从页面触发 → SW 注册并下载 → 进度同步 → 成功后安全落地为可下载文件。其中任意一环缺失(比如忘了 postMessage、没处理 failedRecords、blob 创建后没 revoke)都会导致用户感知为“卡住”或“没反应”。



















