响应式系统不直接处理离线同步,而是高效承接同步结果;需通过封装 IndexedDB 操作为 Composable/Hook、解耦同步逻辑、多标签页事件广播、前置冲突处理,实现 UI 与本地数据一致、网络无关的顺滑离线体验。

响应式系统本身不直接处理离线同步,但它能高效承接同步结果——关键在于把 IndexedDB 的异步数据流,自然接入状态更新机制。Vue 的 reactive / ref 或 React 的 useState / useReducer 本身不感知网络状态,但配合合理的封装和副作用管理,就能让“本地写入→UI即时反馈→后台静默同步”整个链路无缝运转。
用 Composable/Hook 封装数据库操作,让状态自动响应
不要在组件里直接调用 indexedDB.open()。应抽象为可复用的数据访问层,返回响应式数据源:
- Vue 中可写一个 useOfflineStore('cart'),内部监听 IndexedDB 读取完成事件,用
ref暴露最新数据,并在add()、update()方法中触发ref.value = newCart更新视图 - React 中可用 useOfflineData('orders'),内部用
useState存储快照,搭配useEffect监听 IndexedDB 变更(如通过自定义事件或 IDBObserver),实现数据变更即重渲染 - 重点是:所有写操作都先落库再更新状态,避免 UI 和本地数据不一致;读操作优先从内存缓存取,命中失败再查 IndexedDB 并缓存结果
同步逻辑必须解耦,不能卡在响应式更新里
同步不是响应式系统的职责,而是独立的后台任务。常见错误是把 fetch + put 写在 effect 里,导致网络请求失败时 UI 卡顿或报错中断。
- 推荐方案:建立一个 SyncQueue 对象仓库,每次本地修改都生成一条带时间戳、操作类型(add/update/delete)、原始数据的记录
- 用
navigator.onLine监听网络恢复,触发批量同步函数;也可搭配 Service Worker 的sync事件,在浏览器空闲时自动执行 - 同步成功后,再发事件通知相关 Hook 更新状态,或直接调用
store.delete(id)清除队列项——这样 UI 响应和网络逻辑完全分离
多标签页场景下保持状态一致性
用户可能同时打开多个页面,各自修改购物车。仅靠响应式无法感知其他标签页的变更。
- IndexedDB 自身不广播变更,需手动补充机制:在
onupgradeneeded或事务提交后,用window.dispatchEvent(new CustomEvent('idb-change', { detail: { store: 'cart' } })) - 各页面的 Hook 在初始化时监听该事件,收到后主动重新读取对应 store 数据,触发响应式更新
- 进阶做法:用
localStorage做轻量信号(如写入lastCartUpdate=1714468500000),各页面轮询或监听storage事件来判断是否需要刷新
冲突处理要前置到数据模型层
响应式系统只管“显示什么”,不管“该显示哪个版本”。冲突必须在写入 IndexedDB 前就解决。
- 给每条记录加
updatedAt字段,同步时比对服务端时间戳,采用“最后写入者胜”(LWW)策略 - 对强一致性要求高的操作(如下单),使用乐观锁:读取时带
version,提交时校验未变,否则提示用户“数据已被他人修改”并展示差异 - 避免在 UI 层做合并逻辑;应在 IndexedDB 封装层的
update()方法里内置冲突检测与自动合并(如购物车数量取最大值)
不复杂但容易忽略:响应式系统是“显示器”,IndexedDB 是“硬盘”,同步逻辑是“快递员”。三者各司其职,才能让离线体验真正顺滑。

















