background sync 仅在网络恢复时触发一次唤醒信号,真正实现离线数据重发需前端结合 indexeddb 持久化、service worker 手动重建请求及显式错误处理。

Background Sync 本身不自动重发数据,它只是网络恢复时触发一次“唤醒信号”。真正保证离线提交数据重发的,是前端配合 IndexedDB 持久化 + Service Worker 中手动重建请求 + 显式错误处理这一整套逻辑。
必须先存数据,再注册同步
用户点击提交但检测到网络不可用(navigator.onLine === false 或 fetch 失败)时,不能只调用 registration.sync.register('submit-order') 就完事。必须:
- 把请求的关键字段(url、method、headers(序列化)、body(转成字符串或 FormData 兼容格式))提取出来
- 连同时间戳、重试次数、过期时间(如 7 天)一起存入 IndexedDB
- 再调用
sync.register()注册任务——这个动作只是告诉浏览器“等联网后请触发一次 submit-order 事件”
在 Service Worker 中真正执行发送
self.addEventListener('sync', event => { ... }) 是实际干活的地方,这里要:
- 用
event.waitUntil()包裹整个异步流程,防止浏览器提前终止 - 打开 IndexedDB,查询
status = 'pending'且未过期的记录(按创建时间或重试次数排序) - 逐条构造
fetch()请求:用之前存的字段重建 Request,注意设置credentials: 'include'等必要选项 - 成功则更新 DB 记录为
status = 'synced';失败则更新retryCount,超限(如 ≥5)设为'failed'
失败后要主动续约,不是靠浏览器重试
浏览器对每个 sync tag 只保证“至少触发一次”,不会自动重试。所以:
- 如果某条数据发送失败,且还有重试机会,应在 sync handler 内部调用
self.registration.sync.register('submit-order')再次注册 - 避免立即重注册,加个简单退避(如
setTimeout(() => register(), 5000)),防止密集失败打爆服务端 - 每次注册前检查 IndexedDB 是否仍有 pending 数据,没有就别注册,省资源
别忘了权限和环境限制
很多失败其实卡在基础条件上:
- 必须 HTTPS(或 localhost);Chrome 隐身模式默认禁用 Background Sync
- 注册 sync 必须发生在用户真实交互之后(如按钮点击),否则抛
InvalidStateError - 需在网站设置中手动开启“后台同步”权限(地址栏锁图标 → 网站设置 → 后台同步 → 允许)
- Service Worker 必须已激活,且作用域覆盖当前页面路径











