indexeddb需自行实现离线同步,核心是暂存带元信息的操作日志。1. 设计sync_queue存储add/update/delete等操作,含opid、type、table、recordid、data、timestamp、status;2. 离线时先写业务数据再写pending日志;3. 上线后按timestamp批量提交,依响应更新状态或重试;4. 启动时拉取服务端增量变更补全双向同步。

IndexedDB 本身不提供自动同步功能,实现离线数据同步需要你自行设计“本地变更捕获 + 网络恢复后提交 + 服务端响应处理 + 冲突解决”的完整流程。核心不是“用 IndexedDB 存数据”,而是“用它可靠地暂存待同步的操作”。
1. 设计可同步的数据结构:带元信息的操作日志
不要直接同步原始业务数据,而是记录用户执行的“操作”(如 add、update、delete),并附上时间戳、唯一 ID、状态标记:
- opId:客户端生成的 UUID,用于幂等去重
- type:'create' / 'update' / 'delete'
- table:目标表名(如 'todos')
- recordId:业务主键(服务端可能还未分配,可用客户端 UUID 临时占位)
- data:变更后的完整数据(update/delete 可只存 recordId)
- timestamp:毫秒时间戳,用于排序和冲突判断
- status:'pending' / 'synced' / 'failed'(便于重试)
把这些操作存在一个叫 sync_queue 的 objectStore 中,按 timestamp 或 opId 建索引,方便按序提交。
2. 离线时写入 IndexedDB 并标记为 pending
用户新增/修改数据时,先写入业务 store(如 todos),再写一条对应操作日志到 sync_queue,状态设为 pending:
// 示例:添加一条 todo
function addToDoOffline(todo) {
const tx = db.transaction(['todos', 'sync_queue'], 'readwrite');
const todosStore = tx.objectStore('todos');
const queueStore = tx.objectStore('sync_queue');
// 先存业务数据(用客户端 ID)
const clientId = self.crypto.randomUUID();
const saved = todosStore.add({ ...todo, id: clientId });
// 再存同步日志
queueStore.add({
opId: self.crypto.randomUUID(),
type: 'create',
table: 'todos',
recordId: clientId,
data: todo,
timestamp: Date.now(),
status: 'pending'
});
return saved;
}
3. 上线后自动批量同步,并处理响应
监听 online 事件,查询所有 status === 'pending' 的日志,按 timestamp 升序逐条或分批发往服务端:
用于 inference.sh 的 JavaScript/TypeScript SDK,可运行 AI 应用、构建代理、集成 150+ 模型。包名:@inferencesh/sdk(npm install),完整 TypeScript 支持。
- 成功返回 200 且含服务端分配的
id时,更新本地todos中对应记录(替换客户端 ID),并将该操作日志status改为synced - 若服务端返回冲突(如 409),根据业务规则决定覆盖、跳过或合并(例如用
timestamp判定谁更新:本地新则覆盖,服务端新则拉取最新) - 失败(网络中断、5xx)时保持
pending,稍后重试(可用指数退避)
注意:同步过程需防重复提交 —— 提交前将状态临时改为 syncing,失败后再改回 pending;成功后才删或标记 synced。
4. 启动时拉取服务端增量更新(可选但推荐)
仅推本地变更不够,还需拉服务端其他终端的修改。可在首次加载或定期检查时:
- 读取本地已知最新同步时间(如最后一条
synced日志的timestamp) - 请求类似
/api/sync?since=1712345678900获取该时间之后的服务端变更 - 将返回的变更(也是操作日志格式)写入
sync_queue,type设为remote_update,再走同样流程应用到本地todosstore
这样就形成双向同步闭环:本地改 → 推上去,服务端改 → 拉下来。
不复杂但容易忽略:务必给每条操作加唯一 ID 和时间戳,这是去重和判冲突的基础;所有写操作必须在事务中完成,避免部分成功;同步逻辑建议封装成独立模块,与 UI 解耦。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










