IndexedDB封装

写在前面

IndexedDB是一个基于JavaScript的面向对象数据库。目前,前端缓存方案中,Cookie 的大小不超过4KB,且每次请求都会发送回服务器;localStorage 和 sessionStorage 在 2.5MB 到 10MB 之间(各家浏览器不同),所以,需要一种新的解决方案,允许储存大量数据,提供查找接口,还能建立索引,这就是 IndexedDB 诞生的背景。

IndexedDB特点:

  • 储存空间大。IndexedDB 的储存空间比 LocalStorage 大得多,一般来说不少于 250MB,甚至没有上限
  • 异步。针对存储量大的特点,IndexedDB的存储应当是异步进行,以免阻塞js进程
  • 键值对储存。内部采用对象仓库(object store)存放数据。所有类型的数据都可以直接存入,包括 JavaScript 对象。对象仓库中,数据以"键值对"的形式保存,每一个数据记录都有对应的主键,主键是独一无二的,不能重复

封装

为了贴近传统 localStorage 的操作习惯,可以封装出与之对应的api

class IndexedDBCache {
  constructor(dbName, dbVersion, storeName) {
    this._dbName = dbName;
    this._dbVersion = dbVersion;
    this._storeName = storeName;
    this._db = null;
    this._keyPath = '__keyName'; // 设置主键
  }

  /***
   * 打开数据库连接
   * @returns {Promise<IDBDatabase>} 打开的数据库实例
   */
  initDB() {
    return new Promise((resolve, reject) => {
      const request = window.indexedDB.open(this._dbName, this._dbVersion);

      request.onerror = (event) => reject(event.target.error);
      request.onsuccess = (event) => {
        this._db = event.target.result;
        resolve(event.target.result)
      };
      // 创建或更新对象仓库
      request.onupgradeneeded = (event) => {
        // 获取数据库实例
        const db = event.target.result;
        if (!db.objectStoreNames.contains(this._storeName)) {
          db.createObjectStore(this._storeName, { 
            keyPath: this._keyPath, 
            autoIncrement: true 
          });
        }
        this._db = event.target.result;
      };
    });
  }

  /** 保存数据
   * 
   * @param {string} key 
   * @param {any} data 
   * @returns 
   */
  setItem(key, data) {
    const transaction = this._db.transaction([ this._storeName ], 'readwrite');
    const store = transaction.objectStore(this._storeName);
    return new Promise((resolve, reject) => {
      const request = store.put({
        [this._keyPath]: key,
        data
      })
      request.onsuccess = () => resolve(request.result);
      request.onerror = (event) => reject(event.target.error);
    });
  }

  /** 更新数据,合并原始数据
   * 
   * @param {string} key 
   * @param {any} newData 
   * @returns 
   */
  updateItem(key, newData) {
    const transaction = this._db.transaction([ this._storeName ], 'readwrite');
    const store = transaction.objectStore(this._storeName);
    return new Promise((resolve, reject) => {
      const request = store.get(key);
      request.onsuccess = () => {
        let { data } = request.result || {};
        if(this._isObject(newData) && this._isObject(data)) {
          Object.assign(data, newData);
        } else {
          data = newData;
        }
        const updateRequest = store.put({
          [this._keyPath]: key,
          data
        });
        updateRequest.onsuccess = () => resolve(request.result);
        updateRequest.onerror = (event) => reject(event.target.error);
      };
      request.onerror = (event) => reject(event.target.error);
    });
  }

  // 获取数据
  getItem( key ) {
    if(!key) return Promise.resolve();
    const transaction = this._db.transaction([ this._storeName ], 'readonly');
    const store = transaction.objectStore(this._storeName);
    return new Promise((resolve, reject) => {
      const request = store.get(key);
      request.onsuccess = () => {
        const { data } = request?.result || {}
        resolve(data)
      };
      request.onerror = (event) => reject(event.target.error);
    });
  }

  // 读取所有数据
  getAllData() {
    const transaction = this._db.transaction([ this._storeName ], 'readonly');
    const store = transaction.objectStore(this._storeName);
    return new Promise((resolve, reject) => {
      const request = store.getAll();
      request.onsuccess = () => {
        let list = request?.result?.map( i => {
          const { [this._keyPath]: key, ...data } = i;
          return { 
            ...data,
            [this._keyPath]: key
          }
        })
        resolve(list)
      };
      request.onerror = (event) => reject(event.target.error);
    });
  }


  // 删除数据
  removeItem(key) {
    const transaction = this._db.transaction([ this._storeName ], 'readwrite');
    const store = transaction.objectStore(this._storeName);
    return new Promise((resolve, reject) => {
      const request = store.delete(key);
      request.onsuccess = () => resolve(true);
      request.onerror = (event) => reject(event.target.error);
    });
  }

  // 关闭数据库
  closeDB () {
    this._db.close()
  }

  _isObject(obj) {
    return Object.prototype.toString.call(obj) === '[object Object]';
  }
}

使用

实例化并开启
const db = new IndexedDBCache('myDB', 1, 'myStore');
db.initDB()
存数据

由于所有操作都是异步,故该操作应在 initDB() 之后

// await db.initDB()
db.setItem ('用户', { name: '朱棣', age: 135 }).then((key) => {
  console.log('添加成功,主键为:', key);
}).catch((error) => {
  console.error('添加失败:', error);
});
取数据
// await db.initDB()
db.getItem('用户').then((data) => {
  console.log('读取数据:', data);
}).catch((error) => {
  console.error('读取失败:', error);
});
删除数据
// await db.initDB()
db.removeItem('用户').then((data) => {
  console.log('删除成功');
}).catch((error) => {
  console.error('删除失败:', error);
});

--- end ---

posted @ 2024-06-25 10:50  晨の风  阅读(41)  评论(0)    收藏  举报