用 IndexedDB 做离线优先缓存:事务、版本迁移与冲突处理

举报
yd_295628683 发表于 2026/10/25 07:53:42 2026/10/25
【摘要】 离线优先不是把数据塞进 localStorage 就完事。这篇讲清楚 IndexedDB 的事务边界、数据库版本迁移怎么写,以及离线修改和线上数据冲突时怎么合并。

做离线优先的应用,数据层通常绕不开 IndexedDB。它容量比 localStorage 大得多,支持索引和事务,但 API 偏底层,几个关键点没想清楚,后面很容易改不动。

一、先把对象仓库和索引设计好

IndexedDB 是键值数据库,数据按对象仓库(object store)组织。设计时先想清楚查询方式,再决定主键和索引:

const req = indexedDB.open('notes', 1);
req.onupgradeneeded = (e) => {
  const db = e.target.result;
  const store = db.createObjectStore('notes', { keyPath: 'id' });
  store.createIndex('by_updated', 'updatedAt');
  store.createIndex('by_synced', 'synced');
};

主键尽量用客户端生成的 UUID,而不是自增数字。离线创建的数据如果等服务器分配 id,就没法在本地立即关联,体验会很差。

二、事务的生命周期要短

IndexedDB 的事务是自动提交的:一旦当前任务队列里没有新的请求,事务就结束。常见的错误是在事务里 await 一个网络请求,回来再写库,这时事务早就关闭了。

const tx = db.transaction('notes', 'readwrite');
const store = tx.objectStore('notes');
store.put(note);
await txDone(tx); // 只等本地事务

正确做法是把网络请求放在事务外面:先请求成功,再开一个新事务写入。事务里只做本地操作,才能保证它不会莫名其妙地失效。

三、版本迁移要可重复、可回退

数据库版本只能升不能降,onupgradeneeded 里要按 oldVersion 逐级迁移,而不是直接假设从 0 开始:

req.onupgradeneeded = (e) => {
  const db = e.target.result;
  const old = e.oldVersion;
  if (old < 1) {
    db.createObjectStore('notes', { keyPath: 'id' });
  }
  if (old < 2) {
    const store = e.target.transaction.objectStore('notes');
    store.createIndex('by_updated', 'updatedAt');
  }
};

每一步迁移都要考虑已经存在的数据,尤其是新增必填字段时,最好在迁移里给老数据补默认值。

四、离线修改怎么和线上合并

最省心的模型是「最后写入者胜」,但它会悄悄丢数据。稍微稳妥一点的做法是给每条记录加三个字段:updatedAt、synced、deleted。

同步时先拉取服务端变更,再和本地记录比较 updatedAt:本地更新则保留本地并标记待推送,服务端更新则覆盖本地。删除不要直接删记录,而是标记 deleted,等同步完成后再清理,否则离线删除会在下次同步时被服务端的旧数据复活。

if (local.updatedAt > remote.updatedAt) {
  queue.push(local);
} else {
  await store.put(remote);
}

如果业务对冲突敏感,可以保留一份冲突副本让用户选择,而不是自动覆盖。

五、几个容易踩的坑

打开数据库要复用连接。每次操作都 open 一次会拖慢启动,也会在版本升级时卡住。用一个单例 Promise 缓存连接,并在 versionchange 事件里主动关闭。

不要在 Safari 隐私模式下假设可用。部分浏览器在隐私模式下会禁用或限制 IndexedDB,初始化失败时要降级到内存存储,而不是直接白屏。

大文件不要塞进同一条记录。图片、附件建议单独存一个仓库,主记录只存引用,避免一次读取把整个对象都载入内存。

批量写入要合并。几千条记录逐条 await 会很慢,放在同一个事务里批量提交,速度差别非常明显。

六、小结

IndexedDB 的难点不在 API,而在数据模型和同步策略。把主键、索引、版本迁移和冲突规则提前定清楚,离线能力才是可持续的;否则功能越加越多,最后会被事务和版本问题拖住。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。