必须采用「离线暂存 + 网络恢复触发 + 幂等提交」三步闭环实现可靠同步,直接在 onchange 中调用 fetch() 会导致数据丢失、重复提交和失败静默;需结合 indexeddb 暂存、service worker 的 syncmanager 调度及幂等后端接口。

不能靠“编辑完就 fetch 一下”来实现可靠同步,必须拆解成「离线暂存 + 网络恢复触发 + 幂等提交」三步闭环,否则丢数据、重复提交、失败静默是常态。
为什么直接在编辑器 onChange 里调用 fetch() 不行
用户边打字边保存?网络抖动时请求失败、页面意外刷新、浏览器后台冻结都会导致数据丢失。更麻烦的是:fetch() 成功只代表请求发出去了,不代表后端真正落库——比如后端校验失败、幂等键冲突、数据库写入延迟,前端根本收不到反馈。
- 编辑器内容未持久化到
IndexedDB就发请求 → 页面刷新即清空 - 没做防抖或节流 → 频繁输入触发一堆并发
fetch(),后端压力大且顺序难保 - 没处理
response.ok === false场景 → 错误被吞掉,用户以为保存成功 - 没设
credentials: 'include'→ 登录态丢失,401 错误频发(尤其跨子域)
如何用 Service Worker 实现真正的“后台自动同步”
关键不是让编辑器自己发请求,而是把变更交给 SyncManager,由浏览器在下次联网且 SW 激活时统一调度执行。这需要主线程和 SW 协同:
- 主线程:每次内容变更后,先存进
IndexedDB(不能用localStorage,SW 读不到),再调registration.sync.register('editor-auto-save') - Service Worker:监听
sync事件,匹配event.tag === 'editor-auto-save',用event.waitUntil()包裹从 DB 读取 +fetch()提交 + 清理的完整链路 - 必须检查
navigator.serviceWorker.ready再注册,否则抛InvalidStateError - 注册后立刻调
registration.sync.getTags()验证 tag 是否生效,返回空数组说明 SW 已被浏览器终止
fetch() 在 SW 里提交时的硬约束
Service Worker 是无 DOM 环境,fetch() 行为和主线程不同,稍不注意就失败:
- 不能读
window、document或任何全局变量 —— 所有数据必须提前存进IndexedDB并在sync事件中读出 -
fetch()默认不带 Cookie,要传{ credentials: 'include' },否则登录态失效 - 响应体必须显式解析(如
response.json()),不能依赖隐式转换 - 若
fetch()抛错或 Promisereject,浏览器会按指数退避重试(最长可能数小时),但不会通知前端;你得在 SW 里.catch()并记录日志 - 提交失败时别删数据 —— 留着等下次重试;只有
fetch()成功且后端返回 2xx 才清理 DB 记录
编辑器保存逻辑的最小可行闭环示例
以下代码不是完整方案,而是验证各环节是否打通的关键片段:
/* 主线程:编辑器保存触发 */
function saveToBackend(content) {
const id = Date.now();
saveToIDB('pending-edits', { id, content, timestamp: id });
navigator.serviceWorker.ready.then(reg => {
try {
reg.sync.register('editor-auto-save');
} catch (e) {
console.error('Sync register failed:', e);
}
});
}
<p>/<em> sw.js 中 </em>/
self.addEventListener('sync', event => {
if (event.tag === 'editor-auto-save') {
event.waitUntil(
readFromIDB('pending-edits')
.then(items => Promise.all(
items.map(item =>
fetch('/api/save', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
credentials: 'include',
body: JSON.stringify({ id: item.id, content: item.content })
})
.then(res => {
if (res.ok) return res.json();
throw new Error(<code>HTTP ${res.status}</code>);
})
)
))
.then(() => deleteFromIDB('pending-edits'))
.catch(err => console.error('Sync failed:', err))
);
}
});</p>
最常被忽略的一点:readFromIDB() 和 deleteFromIDB() 必须返回 Promise,且整个链路不能漏掉 event.waitUntil() —— 少一个,SW 就可能在任务完成前被系统杀掉。
大量免费API接口:立即使用
涵盖生活服务API、金融科技API、企业工商API、等相关的API接口服务。免费API接口可安全、合规地连接上下游,为数据API应用能力赋能!











