IndexedDB.open()必须配onupgradeneeded才能建结构,否则objectStoreNames.length为0,后续事务报NotFoundError;每次改结构须递增版本号,并在onupgradeneeded中用contains检查后创建store,keyPath与autoIncrement需按数据主键来源正确选择。

indexedDB.open() 必须配 onupgradeneeded 才能建结构
不写 onupgradeneeded,createObjectStore() 就根本不会执行——你拿到的 db 实例里 objectStoreNames.length === 0,后续任何 transaction('users', 'readwrite') 都会报 NotFoundError 或 InvalidStateError: The database has not been upgraded yet.。
这个回调不是“可选升级钩子”,而是唯一能改 schema 的入口。首次打开新库、或传入更高版本号时,浏览器强制触发它;旧版本号不会触发,也不会报错,只会静默沿用旧结构。
- 每次新增 store / index / 修改
keyPath,必须递增版本号(比如从1→2) - 在
onupgradeneeded里先用db.objectStoreNames.contains('users')检查,避免重复创建报ConstraintError - 别在
onsuccess里调createObjectStore()——事务已关闭,直接抛InvalidStateError
createObjectStore() 的 keyPath 和 autoIncrement 怎么选
这两个参数决定主键来源和生成方式,互斥且不可混用。选错会导致 add() 报 DataError 或数据语义错乱。
- 后端返回带明确 ID(如
{ id: "usr_abc123", name: "Alice" })→ 用{ keyPath: 'id' },不设autoIncrement - 纯前端生成、无业务 ID(如本地日志、草稿)→ 用
{ keyPath: 'id', autoIncrement: true },插入时不用传id字段 - 完全无主键字段(如存
Blob或纯配置 JSON)→ 不设keyPath,用store.add(data, key)显式传 key(比如时间戳或 UUID 字符串) - 设了
keyPath: 'id'却传{ name: 'Bob' }(缺id)→add()立即报DataError - 设了
autoIncrement: true还手动传id→ 浏览器忽略你传的值,仍自增
事务必须新建、不能复用,且要监听 oncomplete
IndexedDB 的事务是短命的:一旦当前事件循环结束、且没有待决请求,事务自动 abort。常见表现是批量 add() 只成功第一个,其余全丢。
立即学习“前端免费学习笔记(深入)”;
- 每次读写操作前,必须调用
db.transaction(['storeName'], 'readwrite')新建事务,不能缓存复用 - 写入后务必监听
request.onsuccess或transaction.oncomplete,仅靠add()返回 request 不代表落盘 - 不要在
fetch().then()或setTimeout里操作objectStore——事务早已失效,再调就抛TransactionInactiveError - 批量写入建议用
Promise.all(requests.map(r => new Promise(...)))收集所有onsuccess,而非同步 for 循环
索引不是可选优化项,查非主键字段必须先建 index
objectStore.get(key) 能按主键快速取,但想按 email 或 status 查?没索引就只能遍历全表——大数据量下卡死 UI,count() 和 getAll() 也会变慢。
- 在
onupgradeneeded里,对要查询的字段调用store.createIndex('email_idx', 'email', { unique: false }) - 查索引字段要用
store.index('email_idx').get('a@b.com'),不能直接store.get('a@b.com') -
unique: true适合邮箱、手机号等唯一约束字段;重复值会触发ConstraintError - 索引名(如
'email_idx')只是标识符,不影响查询逻辑,但命名清晰利于维护
最易被忽略的是:版本号不升,onupgradeneeded 就不会触发;而没这个回调,一切结构定义都等于没写。很多“数据库打不开”“查不到数据”的问题,根源都在这里——不是代码漏了某行,而是版本卡在 1 再也没动过。


















