onupgradeneeded是indexeddb唯一可修改数据库结构的时机,仅在首次创建数据库或open时传入更高整数版本号时触发;必须递增版本、检查对象仓库与索引是否存在、结构操作仅限于此回调;旧标签页会阻塞升级,需监听onversionchange并提示用户刷新。

onupgradeneeded 不是可选回调,而是 IndexedDB 唯一允许修改数据库结构的时机。只要没在它里面建好对象仓库(object store)和索引,后续所有读写操作都会失败。
什么时候会触发 onupgradeneeded
只有两种情况会真正执行这个回调:
- 数据库首次创建(从无到有),哪怕版本号是 1,oldVersion 为 0,也会触发
- 调用
indexedDB.open("db", newVersion)且 newVersion 高于当前已存在的版本号
如果版本号不变、或没传版本号(默认为 1)、或传了更低的版本号,它都不会触发——此时你写的 createObjectStore 就像没写一样。
必须递增版本号,不能复用旧值
每次新增对象仓库、删除索引、改 keyPath 或加 autoIncrement,都得升版本。比如从 1 → 2,再从 2 → 3。浏览器靠这个数字判断是否需要走升级流程。
- 版本号必须是整数,不能是字符串或小数
- 写成
indexedDB.open("mydb", 1)却想建新索引?不行——除非之前根本没建过库,否则不会进 onupgradeneeded - 跨版本升级(比如跳过 v2 直接升到 v3)时,要用
oldVersion 而不是 <code>oldVersion === 1来判断逻辑分支
onupgradeneeded 里要做的三件事
这个回调里做的事,决定了你后续能不能正常使用数据库:
-
先检查再创建:用
db.objectStoreNames.contains("users")判断 store 是否已存在,避免重复建导致 ConstraintError -
索引也得检查:创建前用
store.indexNames.contains("email"),删索引前也要先if (store.indexNames.contains("old_idx")) store.deleteIndex("old_idx") - 结构变更只能在这里做:createObjectStore、deleteObjectStore、createIndex、deleteIndex 全部只能放这里——写在 onsuccess 或其他地方会直接报 InvalidStateError
旧标签页会卡住升级,必须主动处理
就算你把版本号改成 3,只要另一个标签页还连着 v2 的数据库,新页面的 onupgradeneeded 就不会触发,而且不报错、不提示,静默失败。
- 在 onsuccess 里立刻给 db 绑定
onversionchange,检测到冲突就db.close() - 配合 UI 提示用户刷新页面,别让旧连接一直占着资源
- 大数据迁移别塞在 onupgradeneeded 里:游标遍历几万条记录会卡死主线程,应拆到独立事务中分批处理











