封装 indexedDB(三)写个插件

因为要在 vue 里面使用,所以做个插件可以方便很多。

项目需求

  • 第一次访问,初始化,需要建立表、导入初始数据
  • 组件获取需要的数据
  • 数据更新的问题

插件功能

  • 在 main 里面安装插件。
  • 建立一个 help, 用 provide 保存,以便于其他组件使用。

定义结构

我们需要先定义一下结构:

{
  dbConfig: db, // 连接字符串,库名和版本
  stores: { // 数据库里的表的集合
    moduleMeta: { // 对象仓库名称
      id: 'moduleId', // keyPath 的名称
      index: {}, // 设置所以
      isClear: false // 是否清空数据
    }
  },
  init (help) {
    // 导入初始数据
  }
  • dbConfig
    设置库名称和版本号

  • stores
    对象仓库的集合,每个属性就是一个对象仓库,依据属性创建对象仓库。

  • init
    传递一个help过来,可以添加数据。

实现插件


import { inject } from 'vue'

import IndexedDB from './help.js'

export default {
  _indexedDBFlag: Symbol('nf-indexedDB-help'),
  _help: {}, // 访问数据库的实例

  /**
   * 根据参数创建一个数据库的实例,初始化数据库
   * * 删表、建表、添加默认数据
   * @param {*} info 参数
   * @returns 安装一个Vue的插件
   * * dbFlag: '数据库标识',
   * * dbConfig: { // 连接数据库
   * * *  dbName: 'vite2-blog',
   * * *  ver: 1.0
   * * },
   * * init: () => {},
   * * stores: {
   * * * storeName: { // 表名
   * * * * id: 'id', // 主键名称
   * * * * index: {
   * * * * * name: ture, // 索引:是否可以重复
   * * * * },
   * * * * isDeleteOldTable: false, // 是否删除之前的表
   * * * }
   * * }
   */
  createHelp (info) {
    let indexedDBFlag = this._indexedDBFlag
    if (typeof info.dbFlag === 'string') {
      indexedDBFlag = Symbol.for(info.dbFlag)
    }
    if (typeof info.dbFlag === 'symbol') {
      indexedDBFlag = info.dbFlag
    }
    // 连接数据库
    const help = new IndexedDB(info)

    return {
      // 安装插件
      install (app, options) {
        // 注入状态,用 symbol 作为标记,避免重名
        app.provide(indexedDBFlag, help)
      }
    }
  },

  // 代码里面调用
  useHelp (_dbFlag) {
    let flag = this._indexedDBFlag
    if (typeof _dbFlag === 'string') {
      flag = Symbol.for(_dbFlag)
    }
    if (typeof _dbFlag === 'symbol') {
      flag = _dbFlag
    }
    if (typeof inject(flag) === 'undefined') {
      // 根级调用
      return this._help
    } else {
      // 子组件调用
      const help = inject(flag)
      return help
    }
  }
}

  • _indexedDBFlag
    Symbol 类型的标识,区分不同的数据库。

  • createHelp
    创建一个 vue 的插件。这里基本没啥代码,就是做个help,然后用 provide 注入一下以便于其他组件也可以使用。
    主要代码还是在 help 的 onupgradeneeded 实现的,这个我也没想到更好的办法。

  • useHelp
    子组件里面获取 help,然后使用这个 help 就可以对 indexedDB 进行操作了。

为啥要弄个 help ?因为想更好的控制初始化和组件读取数据的先后关系。

建库与升级

因为 indexedDB 在初始化的时候会触发 onupgradeneeded ,所以一些代码还是要写在这里。

onupgradeneeded

    // 根据配置信息建立表
    this.dbRequest.onupgradeneeded = (event) => {
      this._dataState = 'loading' // 需要加载
      const db = event.target.result
      console.log('【2】升级数据库 onupgradeneeded --- ', db)

      for (const key in info.stores) {
        const store = info.stores[key]
        // 验证有没有表,没有的话建立一个对象表
        if (!db.objectStoreNames.contains(key)) {
          // const objectStore = db.createObjectStore(object.objectStoreName, { keyPath: 'id' }) /* 创建person仓库(表) 主键 */
          const objectStore = db.createObjectStore(key, { keyPath: store.id }) /* 自动创建主键 autoIncrement: true */
          // 建立索引
          for (const key2 in store.index) {
            const unique = store.index[key2]
            objectStore.createIndex(key2, key2, { unique: unique })
          }
          // if (config.debug) {
          console.log('onupgradeneeded - 建立了一个新的对象仓库:', key)
          // }
        }
      }
    }
    // 根据配置信息建立表
    this.dbRequest.onerror = (event) => {
      // 出错
      console.log('打开数据库出错:', event.target.error)
    }
  }

一开始想在这里添加初始数据的,但是实验了一下发现不行,因为这时候还是“建库”状态,不让填数据。
所以只好把填数据的操作放在 onsuccess 里面。

onsuccess

    // 打开成功,记录连接对象
    this.dbRequest.onsuccess = (event) => {
      this._db = event.target.result
      if (this._dataState === 'none') return

      if (this._dataState === 'loading') {
        // 升级后需要添加初始数据
        if (typeof info.init !== 'function') {
          this._dataState = 'none'
          // 没有设置添加初始化数据的函数
          return
        }

        if (typeof info.init === 'function') {
          // 调用填数据的函数,传入help
          info.init(this)
        }
      }
    }

beginInit

然后我又偷偷的做了一个专门的事务。

  // 初始化填数据的事务
  beginInit (storeName) {
    return new Promise((resolve, reject) => {
      //
      const tranRequest = this._db.transaction(storeName, 'readwrite')
      const store = tranRequest.objectStore(storeName) // 获取store

      tranRequest.onerror = (event) => {
         console.log('读写事务出错:', event.target.error)
         reject('读写事务出错:' + event.target.error)
         tranRequest.abort()
      }

      tranRequest.oncomplete = (event) => {
        // 事务执行完毕
        this._dataState = 'none'
      }
      // 返回对象仓库
      resolve(store)
    })
  }

开启一个读写事务,然后打开对象仓库,把仓库返回去。然后调用者就可以用这个仓库添加对象了。

然后在 oncomplete 里面设置状态。这样就都联系起来了。
好吧有点绕。很容易晕。

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容