IndexedDB索引是实现非主键字段高效查询的唯一可靠方式,必须在onupgradeneeded中创建,支持单字段、复合及multiEntry索引,查询需通过index.get()或openCursor(),且数据须含有效索引字段。

IndexedDB 的索引(Index)是实现非主键字段高效查询的唯一可靠方式。它不是独立结构,而是依附于 objectStore 的辅助查找机制——就像给一本书加目录,主键是页码,索引就是按人名、章节或关键词编排的检索条目。
索引必须在版本升级中创建
createIndex() 只能在 onupgradeneeded 事件里调用,普通事务中执行会直接报 InvalidStateError。每次修改索引结构(新增、删改)都需提升数据库版本号,触发该事件。
- 创建单字段索引:store.createIndex('by-email', 'email', { unique: true })
- 创建复合索引:store.createIndex('by-status-time', ['status', 'createdAt'], { unique: false })
- multiEntry: true 可用于数组字段(如 tags),让每个元素单独生成索引项
索引查询不能用 objectStore.get()
objectStore.get(key) 只认主键。想按 email 查用户,必须先拿到索引对象,再调用其 get() 或 openCursor():
- 查唯一值:const index = store.index('by-email'); index.get('user@demo.com')
- 查多个匹配项:index.openCursor(IDBKeyRange.only('active'))
- 查范围:index.openCursor(IDBKeyRange.bound('2025-01-01', '2025-12-31'))
数据必须包含索引字段,否则不入索引
建了 by-email 索引,但某条记录没写 email 字段,或 email 是 undefined / null,这条数据就不会被该索引覆盖——查询时“消失”,不是 bug,是设计行为。
- 存入前建议校验关键索引字段是否存在且为基本类型(string/number/date)
- 避免对高重复字段(如 isPublished: boolean)建索引,选择性太低,收益小
- 频繁更新的字段(如 lastSeen 时间戳)建索引会拖慢写入,需权衡
复合索引支持排序与分页
按 ['status', 'createdAt'] 建的索引,天然按 status 分组、组内按时间升序排列。配合游标方向和 limit,能高效实现“获取最新 10 条 active 记录”:
- index.openCursor(IDBKeyRange.only('active'), 'prev').onsuccess = e => { ... }
- 遍历时用 cursor.continue() 控制数量,避免一次加载过多
- 结合 IDBKeyRange.lowerBound + count 实现分页,比 skip + limit 更稳定

















