显示页面讨论过去修订反向链接回到顶部 本页面只读。您可以查看源文件,但不能更改它。如果您觉得这是系统错误,请联系管理员。 ====== 第四部分:索引(Index)详解 ====== ===== 4.1 索引概述 ===== 索引是 IndexedDB 中实现高效查询的关键机制。没有索引,你只能通过主键查找数据;有了索引,你可以根据任意属性快速定位记录。 ===== 4.1.1 为什么需要索引 ===== 想象一个包含百万条用户记录的数据库: <code javascript> // 没有索引:只能遍历所有记录 function findUserByEmail(email) { return new Promise((resolve) => { const users = []; const cursor = store.openCursor(); cursor.onsuccess = (event) => { const result = event.target.result; if (result) { if (result.value.email === email) { users.push(result.value); } result.continue(); } else { resolve(users); } }; }); } // 时间复杂度: O(n) // 有索引:直接定位 function findUserByEmailFast(email) { const index = store.index('emailIndex'); return index.get(email); } // 时间复杂度: O(log n) 或 O(1) </code> ===== 4.1.2 索引的工作原理 ===== 索引本质上是一个有序的键值映射: * **键(Key)**:索引属性的值 * **值(Value)**:对应的主键引用 当创建索引时,IndexedDB 会: 1. 遍历对象存储空间中的所有记录 2. 提取索引属性的值 3. 构建 B-Tree 结构(具体实现因浏览器而异) 4. 维护索引与数据的同步 ===== 4.2 创建索引 ===== ===== 4.2.1 createIndex 方法 ===== <code javascript> // 基本语法 const index = store.createIndex(indexName, keyPath, options); // options { unique: boolean, // 是否唯一 multiEntry: boolean // 是否多入口(用于数组值) } </code> ===== 4.2.2 索引类型详解 ===== **普通索引**: <code javascript> // 允许重复值 store.createIndex('ageIndex', 'age', { unique: false }); // 可以获取所有 25 岁的用户 const request = index.getAll(25); </code> **唯一索引**: <code javascript> // 不允许重复值 store.createIndex('emailIndex', 'email', { unique: true }); // 尝试插入重复值会报错 store.add({ id: 2, email: 'existing@example.com' }); // ConstraintError: 该 email 已存在 </code> **多入口索引(MultiEntry)**: <code javascript> // 用于数组类型的属性 store.createIndex('tagIndex', 'tags', { multiEntry: true }); // 数据 store.add({ id: 1, title: 'IndexedDB 教程', tags: ['javascript', 'database', 'browser'] }); // 查询 const request = index.getAll('javascript'); // 返回所有包含 'javascript' 标签的文章 </code> **复合索引**: <code javascript> // 在多个属性上创建索引 store.createIndex('nameAgeIndex', ['lastName', 'firstName', 'age']); // 查询方式 index.get(['张', '三', 25]); </code> ===== 4.3 索引查询 ===== ===== 4.3.1 基础查询方法 ===== <code javascript> const index = store.index('ageIndex'); // 获取第一条匹配记录 index.get(25).onsuccess = (e) => { console.log('第一个25岁用户:', e.target.result); }; // 获取所有匹配记录 index.getAll(25).onsuccess = (e) => { console.log('所有25岁用户:', e.target.result); }; // 获取匹配记录的数量 index.count(25).onsuccess = (e) => { console.log('25岁用户数量:', e.target.result); }; // 只获取键(不包含完整对象) index.getKey(25).onsuccess = (e) => { console.log('主键:', e.target.result); }; </code> ===== 4.3.2 范围查询 ===== 使用 IDBKeyRange 进行范围查询: <code javascript> // 创建范围 const range = IDBKeyRange.bound(lower, upper, lowerOpen, upperOpen); // 范围类型 IDBKeyRange.only(value); // 等于 value IDBKeyRange.lowerBound(value); // >= value IDBKeyRange.upperBound(value); // <= value IDBKeyRange.bound(lower, upper); // lower <= x <= upper IDBKeyRange.bound(lower, upper, true); // lower < x <= upper IDBKeyRange.bound(lower, upper, false, true); // lower <= x < upper // 示例:查询年龄在 20 到 30 之间的用户 const ageIndex = store.index('ageIndex'); const range = IDBKeyRange.bound(20, 30); const request = ageIndex.getAll(range); // 示例:查询名字在字典序 "A" 到 "M" 之间的用户 const nameIndex = store.index('nameIndex'); const nameRange = IDBKeyRange.bound('A', 'M', false, true); const request2 = nameIndex.openCursor(nameRange); </code> ===== 4.4 索引管理 ===== ===== 4.4.1 删除索引 ===== <code javascript> request.onupgradeneeded = function(event) { const db = event.target.result; const transaction = event.target.transaction; // 获取对象存储空间 const store = transaction.objectStore('users'); // 删除索引 if (store.indexNames.contains('oldIndex')) { store.deleteIndex('oldIndex'); console.log('索引已删除'); } }; </code> ===== 4.4.2 获取索引信息 ===== <code javascript> const index = store.index('ageIndex'); // 索引名称 console.log(index.name); // 'ageIndex' // 索引的 keyPath console.log(index.keyPath); // 'age' // 是否多入口 console.log(index.multiEntry); // false // 是否唯一 console.log(index.unique); // false // 关联的对象存储空间 console.log(index.objectStore); // IDBObjectStore </code> ===== 4.5 索引设计最佳实践 ===== ===== 4.5.1 选择合适的索引属性 ===== **应该创建索引的属性:** * 经常用于查询条件的属性 * 需要排序的属性 * 外键关联的属性 **不应该创建索引的属性:** * 很少用于查询的属性 * 值高度唯一的属性(如果已经是主键) * 大文本字段 * 频繁更新的属性(索引维护有开销) ===== 4.5.2 索引命名规范 ===== <code javascript> // 推荐命名格式:[属性名]Index store.createIndex('emailIndex', 'email', { unique: true }); store.createIndex('createdAtIndex', 'createdAt', { unique: false }); // 复合索引命名 store.createIndex('nameAgeIndex', ['lastName', 'age']); store.createIndex('categoryPriceIndex', ['category', 'price']); </code> ===== 4.5.3 索引数量控制 ===== 索引不是越多越好: * 每个索引都会占用额外存储空间 * 写入操作需要维护所有相关索引 * 建议每个对象存储空间索引数不超过 10 个 ===== 4.6 本章小结 ===== 本章深入介绍了 IndexedDB 的索引机制: * 索引用于加速非主键属性的查询 * 支持普通索引、唯一索引、多入口索引和复合索引 * 使用 IDBKeyRange 进行范围查询 * 索引设计需要平衡查询性能和存储开销 继续阅读 [[part05-crud|第五部分:CRUD 操作详解]]。 登录 Detach Close 该主题尚不存在 您访问的页面并不存在。如果允许,您可以使用创建该页面按钮来创建它。 indexeddb/part04-index.txt 最后更改: 2026/04/27 19:51由 张叶安 登录