IndexedDB封裝
重點
數據庫初始創建或更新后會先觸發onupgradeneeded方法,然后再觸發onsuccess方法,如果在onupgradeneeded方法中執行了表結構操作的話,onsuccess會在transaction.oncomplete事件處理之后觸發,所以在onsuccess方法中去執行增刪改一定是在表完整結構基礎上不必擔心表錯誤
代碼
export default class IndexedDB {
/** 表名 */
private tableName: string;
/** 鍵列表,第一個為主鍵,其余為索引鍵 */
private keyList: string[];
/** 等待連接數據庫的 Promise */
awaitOpenDB: Promise<IDBDatabase>;
// 構造方法
constructor(
tableName: 'indexedTable',
keyList: string[],
dbName: 'indexedDB'
) {
this.tableName = tableName;
this.keyList = keyList;
this.awaitOpenDB = this.openDB(dbName);
}
/** 連接數據庫并返回 Promise */
private openDB(dbName: string): Promise<IDBDatabase> {
return new Promise((resolve, reject) => {
const request = window.indexedDB.open(dbName);
// 創建或更新數據庫時調用
request.onupgradeneeded = (e) =>
this.initDB((e.target as IDBOpenDBRequest).result);
// 連接成功
request.onsuccess = (e) => resolve((e.target as IDBOpenDBRequest).result);
// 連接失敗
request.onerror = (e) => reject((e.target as IDBOpenDBRequest).error);
});
}
/** 初始化數據庫 */
private initDB(db: IDBDatabase) {
// 拆出主鍵和索引鍵
const [keyPath, ...otherKey] = this.keyList;
// 創建創建對象存儲(表)和主鍵
const objStore = db.createObjectStore(this.tableName, {
keyPath: keyPath || 'id',
autoIncrement: true,
});
// 創建索引
otherKey.forEach((key) => {
if (!objStore.indexNames.contains(key)) {
objStore.createIndex(key, key, { unique: false });
}
});
}
/** 設置一條數據 */
private async setItem(obj: Record<string, any>): Promise<void> {
const db = await this.awaitOpenDB;
return new Promise((resolve, reject) => {
const request = db
.transaction([this.tableName], 'readwrite')
.objectStore(this.tableName)
.put(obj); // 插入或更新數據
request.onsuccess = () => resolve();
request.onerror = () => reject(new Error('設置數據失敗'));
});
}
/** 設置數據 */
public async setData(list: Array<Record<string, any>> | Record<string, any>) {
const arr = Array.isArray(list) ? list : [list]; // 統一處理為數組
return Promise.allSettled(arr.map((v) => this.setItem(v)));
}
/** 獲取數據 */
public async getData(
obj: Record<string, any>
): Promise<Array<Record<string, any>>> {
const db = await this.awaitOpenDB;
const objectStore = db
.transaction(this.tableName)
.objectStore(this.tableName);
const objKeyList = Object.keys(obj); // 獲取查詢條件的鍵
const results: Array<Record<string, any>> = [];
return new Promise((resolve) => {
objectStore.openCursor().onsuccess = (event) => {
const cursor = (event.target as IDBRequest).result;
if (cursor) {
if (objKeyList.every((v) => obj[v] === cursor.value[v])) {
results.push(cursor.value); // 匹配則添加到結果中
}
cursor.continue(); // 繼續遍歷
} else {
resolve(results); // 遍歷結束,返回結果
}
};
});
}
/** 通過主鍵刪除數據 */
public async removeItem(keyPath: string): Promise<void> {
const db = await this.awaitOpenDB;
return new Promise((resolve, reject) => {
const request = db
.transaction([this.tableName], 'readwrite')
.objectStore(this.tableName)
.delete(keyPath); // 根據主鍵刪除數據
request.onsuccess = () => resolve();
request.onerror = () => reject(new Error('刪除數據失敗'));
});
}
/** 刪除符合條件的數據 */
public async removeData(obj: Record<string, any>) {
// 由于只能通過主鍵刪除數據,所以要先根據條件進行查詢,后逐條進行刪除操作
const res = await this.getData(obj);
return Promise.allSettled(
res.map((v) => this.removeItem(v[this.keyList[0]]))
);
}
}
使用
storageDB = new indexedDB('表名',['id','name'],'庫名')
// indexedDBClass中參數都非必填
// 默認表名indexedTable
// 默認表名字段列表['id']
// 默認庫名indexedDB
// 使用數據庫的代碼都要在這里面運行,不然會出現數據庫未鏈接就使用的情況
// 獲取數據(參數是匹配條件)
let list = await storageDB.getData({ name: '張三' })
// 新增&插入數據(參數是完整的要存儲的數據)
await storageDB.setData({ name: '李四' })
// 刪除數據(參數是匹配條件)
await storageDB.removeData({ name: '張三' })
})
評價
indexedDb有點類似于storage的存儲方式,特別是在存取單條數據的時候幾乎寫法大差不差
對于批量操作沒有任何可用的api,只能化整為零逐條操作
查詢功能更是一塌糊涂,僅支持單個鍵全匹配或者獲取全部,是不是跟storage獲取超級相似,跟sql庫完全沒有可比性

浙公網安備 33010602011771號