IndexedDB 版本升级是结构变更的强制契约,必须通过 onupgradeneeded 显式处理迁移;不支持就地修改对象仓库或索引,每次升级需校验 oldVersion 并执行创建/删除操作。

IndexedDB 的数据结构设计不是一锤定音的事,而是随业务演进持续迭代的过程。版本号不只是数字,它是数据库 schema 变更的“契约”——每次结构调整都必须通过升级版本触发 onupgradeneeded,否则旧结构无法自动适配新逻辑,轻则查询失败,重则数据不可读。
版本变更必须显式处理结构迁移
IndexedDB 不支持“就地修改”对象仓库或索引。比如从 v1 升级到 v2 时新增一个 status 字段并为其建索引,不能只改 JS 对象属性;必须在 onupgradeneeded 中完成三件事:
- 检查
oldVersion是否低于目标版本(如if (oldVersion ),避免重复执行 - 获取当前事务中的 objectStore(
transaction.objectStore('users')),不能直接用db.createObjectStore重建已有仓库 - 调用
createIndex('status', 'status')添加新索引;若需迁移历史数据(如给旧记录补默认值),需手动遍历游标并put()更新
字段增删要兼顾读写双端兼容
前端没有服务端那样的强契约约束,JS 对象字段缺失不会报错,但容易埋下隐性 bug。例如 v1 版本用户对象含 {id, name, email},v2 新增 avatarUrl 并设为可选:
-
写入端:插入新数据时可带
avatarUrl,老代码不传该字段也能成功写入(只要没设required校验) -
读取端:必须主动判空,如
user.avatarUrl ?? '/default.png',不能假设字段一定存在 -
删除字段(如移除
legacyToken)更需谨慎:旧数据仍保留该字段,但新逻辑应忽略;若彻底清理,需在升级事务中用游标批量remove()或put()覆盖
索引策略影响查询兼容性与性能
索引不是越多越好,但缺失关键索引会让查询退化为全表扫描。版本迭代中调整索引要注意:
- 新增索引仅加速对应字段的查询,不影响已有数据结构,可安全添加
- 删除索引前确认无代码依赖它(如
index.get()或index.openCursor()),否则运行时报错 “Index not found” - 修改索引选项(如将非唯一索引改为
{ unique: true })属于破坏性变更,必须先删除再重建,并处理重复数据冲突
多版本共存时的数据桥接设计
真实场景中,用户可能长期不刷新页面,导致同一域名下多个标签页运行不同版本代码。这时数据库版本可能不一致:
- v1 页面打开数据库时,会拿到旧版 db 实例,无法使用 v2 新增的索引或字段
- 建议在初始化阶段检查
window.db.version是否匹配当前代码期望版本,不匹配则提示刷新或自动触发升级(注意:升级需所有标签页配合,可用versionchange事件监听并 reload) - 对核心字段做“软兼容”,例如用
data?.profile?.bio || data.bio支持嵌套结构变化,降低版本切换冲击

















