如何使用idb包编写通用的按索引获取记录函数(结合const as const避免硬编码)
如何使用idb包编写通用的按索引获取记录函数(结合const as const避免硬编码)
嘿,你已经用const object as const来规避硬编码字符串了,这步真的很明智!既保证了代码的可维护性,又能让TypeScript帮我们做类型检查。接下来我就带你一步步封装一个通用的按索引获取idb记录的函数,彻底和硬编码说拜拜。
首先,先把你给出的代码补全并梳理清楚,确保我们的常量和数据库Schema完全对应:
import { Injectable } from '@angular/core'; import { openDB, DBSchema, IDBPDatabase, IDBKeyRange } from 'idb'; // 用as const把字符串锁定为字面量类型,彻底杜绝硬编码 const ObjectStoreName = { SummaryData: 'SummaryData', TestData: 'TestData', } as const; const ObjectStoreIndex = { ByTimestamp: 'ByTimestamp', BySizeAndName: 'BySizeAndName', } as const; // 定义SummaryData对应的数据结构 interface SummaryFileCache { key: string; timestamp: number; size: number; name: string; } // 定义数据库完整Schema,关联对象仓库、数据结构与索引 interface MyDBSchema extends DBSchema { [ObjectStoreName.SummaryData]: { key: string; value: SummaryFileCache; indexes: { [ObjectStoreIndex.ByTimestamp]: number; // 单字段索引对应类型 [ObjectStoreIndex.BySizeAndName]: [number, string]; // 复合索引对应类型 }; }; [ObjectStoreName.TestData]: { key: string; value: { id: string; category: string; count: number }; indexes: { [ObjectStoreIndex.ByTimestamp]: number; }; }; }
接下来我们在Angular服务里封装数据库操作,核心就是那个通用的按索引查询函数:
@Injectable({ providedIn: 'root' }) export class DbService { private dbPromise: Promise<IDBPDatabase<MyDBSchema>>; constructor() { // 初始化数据库,创建对象仓库与索引 this.dbPromise = openDB<MyDBSchema>('MyDatabase', 1, { upgrade(db) { // 创建SummaryData仓库及其索引 if (!db.objectStoreNames.contains(ObjectStoreName.SummaryData)) { const summaryStore = db.createObjectStore(ObjectStoreName.SummaryData, { keyPath: 'key' }); summaryStore.createIndex(ObjectStoreIndex.ByTimestamp, 'timestamp'); summaryStore.createIndex(ObjectStoreIndex.BySizeAndName, ['size', 'name']); } // 创建TestData仓库及其索引 if (!db.objectStoreNames.contains(ObjectStoreName.TestData)) { const testStore = db.createObjectStore(ObjectStoreName.TestData, { keyPath: 'id' }); testStore.createIndex(ObjectStoreIndex.ByTimestamp, 'timestamp'); } }, }); } /** * 通用按索引获取记录的函数 * @param storeName 目标对象仓库(只能传预定义的ObjectStoreName值) * @param indexName 目标索引(只能传对应仓库存在的索引,来自ObjectStoreIndex) * @param query 查询条件:可以是具体值,也可以是IDBKeyRange(比如范围查询) * @param count 传true返回匹配数量,默认false返回所有匹配记录 */ async getRecordsByIndex<StoreName extends keyof MyDBSchema>( storeName: StoreName, indexName: keyof MyDBSchema[StoreName]['indexes'], query: IDBKeyRange | IDBValidKey, count = false ): Promise<Array<MyDBSchema[StoreName]['value']> | number> { const db = await this.dbPromise; const tx = db.transaction(storeName, 'readonly'); const store = tx.objectStore(storeName); // 这里的as string是适配idb底层类型要求,我们已经通过泛型确保了indexName的合法性 const index = store.index(indexName as string); if (count) { return index.count(query); } return index.getAll(query); } }
现在来看看怎么用这个函数,完全不用写任何硬编码字符串,TypeScript还会自动帮我们做类型提示:
// 示例1:获取SummaryData中2024年1月1日之后的所有记录 async getRecentSummaryRecords() { const startTimestamp = new Date('2024-01-01').getTime(); // 用IDBKeyRange实现范围查询 const timeRange = IDBKeyRange.lowerBound(startTimestamp); // 只能传预定义的常量,写错会直接在编译阶段报错 const records = await this.dbService.getRecordsByIndex( ObjectStoreName.SummaryData, ObjectStoreIndex.ByTimestamp, timeRange ); console.log('近期汇总记录:', records); } // 示例2:获取TestData中时间戳等于指定值的记录数量 async getTestRecordCount(timestamp: number) { const count = await this.dbService.getRecordsByIndex( ObjectStoreName.TestData, ObjectStoreIndex.ByTimestamp, timestamp, true ); console.log(`匹配的测试记录数:${count}`); }
最后说下这个方案的核心优势:
- 彻底告别硬编码:所有仓库名和索引名都来自预定义常量,再也不会出现拼写错误或者后续修改名称时漏改的情况;
- 类型安全拉满:TypeScript会根据你传入的仓库名自动推断返回数据类型,传错索引名会直接编译报错;
- 高度复用:这个通用函数能处理所有仓库的索引查询需求,不用为每个仓库写重复代码。
内容来源于stack exchange
相关产品推荐
相关产品推荐

