前端工具——indexedDB

380 阅读2分钟

简介

IndexedDB 是浏览器提供的 客户端 NoSQL 数据库,用于存储大量结构化数据(如 JSON 对象、文件二进制数据等)。与 localStorage 相比,它具备以下优势:

  • 存储容量大:通常支持数百MB甚至GB级数据(取决于浏览器)。
  • 事务支持:保证数据操作的原子性和一致性。
  • 异步 API:非阻塞主线程,适合处理复杂操作。
  • 索引查询:支持高效检索,类似传统数据库。

基本操作

  1. 打开一个数据库。
  2. 在数据库中创建一个对象存储。
  3. 启动一个事务并请求执行一些数据库操作,例如添加或检索数据。
  4. 通过侦听正确的DOM事件来等待操作完成。
  5. 对结果做一些事情(可以在请求对象上找到)。

构造函数

 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);
       }
   }