简介
IndexedDB 是浏览器提供的 客户端 NoSQL 数据库,用于存储大量结构化数据(如 JSON 对象、文件二进制数据等)。与
localStorage相比,它具备以下优势:
- 存储容量大:通常支持数百MB甚至GB级数据(取决于浏览器)。
- 事务支持:保证数据操作的原子性和一致性。
- 异步 API:非阻塞主线程,适合处理复杂操作。
- 索引查询:支持高效检索,类似传统数据库。
基本操作
- 打开一个数据库。
- 在数据库中创建一个对象存储。
- 启动一个事务并请求执行一些数据库操作,例如添加或检索数据。
- 通过侦听正确的DOM事件来等待操作完成。
- 对结果做一些事情(可以在请求对象上找到)。
构造函数
private _db!: IDBDatabase;
private _version!: number;
private _database!: string;
private _storeName!: string;
private _openLog!: boolean;
/**
* Constructor a new indexedDB object
* @param database database name
* @param version database version
* @param storeName store object name
* @param openLog - 是否打印 indexedDB 变化
*/
constructor(database: string, version: number, storeName: string, openLog = false) {
if (!('indexedDB' in window)) {
console.log("This browser doesn't support IndexedDB");
} else {
this._storeName = storeName;
this._version = version;
this._database = database;
this._openLog = openLog;
}
}
打开数据库
const request = LocalIndexedDB.open(数据库名, 数据库版本);
public open() {
return new Promise<IDBDatabase>((resolve, reject) => {
try {
const self = this;
if (self._db) {
return resolve(self._db);
}
// If exist the same version database, there need upgrade an new version database,
// because of the same version can't trigger onupgradeneeded event which will occur
// object stores was not found exception.
const request = indexedDB.open(self._database, self._version);
request.onsuccess = function () {
self._db = request.result;
self._db.onversionchange = (event: any) => {
console.log('onversionchange', event);
};
resolve(request.result);
console.log('Open indexedDB success!');
};
// onupgradeneeded -> transaction.oncomplete -> onsuccess
request.onupgradeneeded = function (e: any) {
console.log('openDb.onupgradeneeded', e);
self._db = request.result;
if (!self._db.objectStoreNames.contains(self._storeName)) {
const objectStore = self._db.createObjectStore(self._storeName);
objectStore.transaction.oncomplete = function () {
resolve(request.result);
};
}
};
request.onblocked = function (e: any) {
console.log('openDb onblocked', e);
};
request.onerror = function (e: Event) {
console.log('Maybe you not allow my web app to use IndexedDB!');
reject(e);
};
} catch (e) {
console.error(e);
reject(e);
}
});
}
定义事物模式
要读取现有对象存储的记录,事务可以处于readonly或readwrite模式。要更改现有的对象库,事务必须处于readwrite模式。您使用打开此类交易IDBDatabase.transaction。该方法接受两个参数:(storeNames范围,定义为要访问的对象存储的数组)和事务的mode(readonly或readwrite)。该方法返回一个包含该IDBIndex.objectStore方法的事务对象,可用于访问对象存储。默认情况下,如果未指定任何模式,则事务将以readonly模式打开。
private getObjectStore(storeName: string, mode: IDBTransactionMode) {
if (this._db) {
const transaction = this._db.transaction([storeName], mode);
return transaction.objectStore(storeName);
}
return null;
}
增删查改
public add(value: any, key?: string) {
return this.wrapStoreOperationPromise(function (store: IDBObjectStore) {
return store.add(value, key);
});
}
/**
* Set a value to store object by key
* @param key the key of store object
* @param value the value of store object
*/
public set(key: string, value: any) {
this.log('IndexedDB set', key, value);
return this.wrapStoreOperationPromise(function (store: IDBObjectStore) {
return store.put(value, key);
});
}
/**
* Get the value with the given key
* @param key the key of store object
*/
public get(key: string) {
this.log('IndexedDB get', key);
return this.wrapStoreOperationPromise(function (store: IDBObjectStore) {
return store.get(key);
});
}
/**
* Delete records in store with the given key
* @param key the key of store object
*/
public delete(key: string) {
return this.wrapStoreOperationPromise(function (store: IDBObjectStore) {
return store.delete(key);
});
}
/**
* Delete all data in store object
*/
public clear() {
return this.wrapStoreOperationPromise(function (store: IDBObjectStore) {
return store.clear();
});
}
/**
* Get the store object
*/
public getStore() {
return this.getObjectStore(this._storeName, 'readwrite');
}
外部存储
public wrapStoreOperationPromise<T = IDBRequest>(
operate: (store: IDBObjectStore) => IDBRequest
): Promise<T> {
return new Promise((resolve, reject) => {
try {
const store = this.getObjectStore(this._storeName, 'readwrite');
if (store) {
const req = operate(store);
req.onsuccess = (evt: any) => resolve(evt.target.result);
req.onerror = (evt: any) => reject(evt);
}
} catch (e) {
console.error(e);
reject(e);
}
});
}
private log(...args: any) {
if (this._openLog) {
console.log(...args);
}
}