HTML5离线同步树状数据需三端协同:客户端用IndexedDB存结构化树(含is_dirty、sync_status等字段),Service Worker按节点粒度分批提交并退避重试,服务端支持幂等UPSERT与冲突校验。

HTML5 中处理树状嵌套业务数据的离线同步,不能简单套用扁平表单逻辑。核心难点在于:数据结构深(多层父子关系)、提交粒度不确定(整棵树?局部节点?)、状态需独立追踪(某子节点失败不能阻塞其余)、服务端校验复杂(如父子ID依赖、路径唯一性)。必须在客户端存储、同步调度与服务端协作三端协同设计。
用 IndexedDB 存结构化树数据,而非字符串拼接
localStorage 无法支撑嵌套数据的可靠暂存——它不支持事务、无查询能力、写入大对象易静默失败。树状数据天然含层级、引用和变更标记,必须用 IndexedDB:
- 为每棵业务树建独立 objectStore(如 tree_orders),字段至少包含:
id(客户端生成 UUID)、root_id(根节点标识)、data(完整 JSON 树,含 nodes 数组、parent_id 字段、is_dirty 标记)、status(pending / syncing / synced / failed)、updated_at - 所有写入必须包裹在事务中,尤其涉及父子节点同时修改时(如拖拽重排),避免中间态残留
- 为支持局部提交(如仅提交某个子分支),data 结构中每个节点应带
sync_status: 'pending' | 'synced' | 'ignored',同步逻辑只拉取 pending 节点子树
Service Worker 同步需按树粒度拆解与重试
Background Sync 不适合直接发一棵大 JSON 树——超时风险高、失败后难以定位具体节点。应将树结构“扁平化切片”再分批同步:
- 在 sync 事件中,从 IndexedDB 读出一条
status = 'pending'的树记录,解析其 data,提取所有sync_status === 'pending'的节点 - 按父子依赖顺序排序(先父后子),生成待提交队列;对每个节点构造独立 POST 请求(如
/api/tree-node),携带client_node_id和parent_client_id - 单个节点失败时,只标记该节点为 failed,不影响队列中其余节点;记录失败原因(如 409 冲突)并暂停后续依赖节点,等下次 sync 重试
- 加入重试退避:首次失败后 1 分钟重试,二次失败后 5 分钟,三次后提示用户手动检查
前端 UI 必须暴露树级同步状态与干预入口
用户需要明确知道哪部分已上云、哪部分卡住、能否跳过或修正。不能只显示“同步中”:
立即学习“前端免费学习笔记(深入)”;
- 在树组件旁加状态栏:显示“根节点 ✅|子项A ⏳|子项B ❌(权限不足)”,点击 ❌ 图标可查看错误详情和重试按钮
- 提供「暂存快照」管理页:列出所有 pending 树记录,按时间倒序,显示 root_name、节点数、最后修改时间,支持批量重试或删除
- 编辑任意节点时自动标记其自身及所有祖先节点为
is_dirty = true,但不立即触发同步——由用户显式点击“提交变更”或离开页面时统一暂存 - 网络恢复后,不自动全量提交;优先同步用户最近操作的那棵活跃树,其余延后
服务端必须支持树形幂等与冲突合并
前端切片提交,服务端需能识别并安全合并同一棵树的多次变更:
- 接收请求时,校验
client_node_id+root_id组合是否已存在;若存在且updated_at更旧,则拒绝并返回 409,前端据此标记为 conflict - 支持 UPSERT 接口:POST
/api/tree-node时,若parent_client_id对应的父节点尚未创建,先创建父节点并返回其服务端 ID,再创建当前节点 - 响应体必须返回完整节点信息,含服务端生成的
server_id和server_updated_at,前端收到后更新 IndexedDB 中对应节点字段,保持双端 ID 映射 - 提供「树校验」端点(如
GET /api/tree/{root_id}/health),供前端在同步完成后主动调用,确认整棵树状态一致



















