background sync 仅是网络恢复时触发的“唤醒钩子”,非自动重试机制;需前端主动暂存请求至 indexeddb 并手动重发,且 request 对象须序列化关键字段后存储。

Background Sync 本身不能处理断网期间的提交
它只在网络恢复且 Service Worker 处于活跃状态时触发一次,不是“断网自动重试”机制。你看到的「断网也能提交」效果,其实是前端主动把请求暂存到 IndexedDB,再等 sync 事件触发后手动重发——Background Sync 只是那个“唤醒钩子”,不是队列管理器。
常见错误是直接在 fetch 事件里拿到 Request 对象就塞进数据库,结果同步时读出来无法重建请求:因为 Request 是流式对象,不可序列化。必须提前提取 url、method、headers 和 body(转成字符串或 FormData 兼容格式),再存入 IndexedDB。
注册 sync 任务前必须检查运行环境
Chrome 要求用户至少有一次真实交互(比如点击按钮、提交表单)后才能注册,否则 registration.sync.register('submit-order') 会抛出 InvalidStateError: Registration failed - no active window;隐身模式、HTTP 环境、Service Worker 未激活也会失败。
- 用
try/catch包裹注册逻辑,捕获DOMException并判断err.name是'InvalidStateError'还是'NotAllowedError' - 注册时机应放在用户点击「提交」且前端确认离线/请求失败之后,而不是页面加载时盲目调用
- tag 名建议带业务和版本,比如
'submit-order-v2',避免灰度升级时旧任务干扰新逻辑
sync 事件里必须自己实现重试与状态更新
浏览器不保证 sync 一定成功,也不提供重试次数、退避策略或失败回调。所有这些都得你自己写:
- 从
IndexedDB读取status = 'pending'的订单,按created_at或重试计数排序 - 逐条
fetch,成功则用事务更新记录:设status = 'synced'、写入server_order_id和synced_at - 失败时更新
retryCount,若超过阈值(如 5 次)可设为'failed'并记录lastError - 每次操作前检查 DB 连接是否仍有效(长时间后台后可能已关闭),同步完成后主动调用
db.close()
容易被忽略的关键细节
最常漏掉的是对 pending 数据加过期机制。用户可能半年没打开页面,本地积压几十条失效订单,既占配额又影响同步性能。建议在写入时加 expires_at = Date.now() + 7 * 24 * 60 * 60 * 1000,同步前先过滤掉过期项。
另一个隐形坑是幂等性完全依赖服务端配合:前端传的客户端 id 必须被后端用于 UPSERT 或唯一约束校验,否则重复 sync 可能生成多笔相同订单。别指望 Background Sync 自己去重。










