vue3 专用 indexedDB 封装库,基于Promise告别回调地狱(二)

简介: https://developer.mozilla.org/zh-CN/docs/Web/API/IndexedDB_API 这个大概是官网吧,原始是英文的,现在陆续是出中文版。有空的话还是多看看官网。

使用方式

看了上面的代码可能会感觉很晕,这么复杂?不是说很简单吗?

对呀,把复杂封装进去了,剩下的就是简单的调用了。那么如何使用呢?


准备创建数据库的信息

我们先定义一个对象,存放需要的各种信息


const dbInfo = {
  dbFlag: 'project-meta-db', // 数据库标识,区分不同的数据库。如果项目里只有一个,那么不需要加这个标识
  dbConfig: {
    dbName: 'nf-project-meta', // 数据库名称
    ver: 2
  },
  stores: { // 数据库里的表(对象仓库)
    moduleMeta: { // 模块的meta {按钮,列表,分页,查询,表单若干}
      id: 'moduleId',
      index: {},
      isClear: false
    },
    menuMeta: { // 菜单用的meta
      id: 'id',
      index: {},
      isClear: false
    },
    serviceMeta: { // 后端API的meta,在线演示用。
      id: 'moduleId',
      index: {},
      isClear: false
    }
  },
  init: (help) => {
    // 数据库建立好了
    console.log('inti事件触发:indexedDB 建立完成 ---- help:', help)
  }
}


  • dbFlag 一个项目里面可能需要同时使用多个 indexedDB 的数据库,那么就需要一个标识区分一下,dbFlag 就是区分标识。
  • stores 对象仓库的说明,在 onupgradeneeded 事件里面依据这个信息创建对象仓库。
  • init indexedDB 都准备好之后的回调函数。


直接使用


import IndexedDB from'../../../packages/nf-ws-indexeddb/help.js'
// 建立实例
const help = new IndexedDB(dbInfo)
// 添加对象的测试
const add = () => {
  // 定义一个对象
  const model = {
    id: newDate().valueOf(),
    name: 'test'
  }
  // 添加
  help.addModel('menuMeta', model).then((res) => {
    console.log('添加成功!', res) // 返回对象ID
  })
}
  • 定义一个数据库描述信息
  • 生成 help 的实例
  • 使用 help.addModel 添加对象


做个“外壳”套个娃


检查一下代码,发现有几个小问题:

  • 每次使用都需要实例化一个help吗?是不是有点浪费?
  • 对象仓库名还需要写字符串,万一写错了怎么办?
  • help.xxxModel(xxx,xxx,xxx) 是不是有点麻烦?

所以我们需要在套一个外壳,让使用更方便。


import IndexedDB from'./help.js'
/**
 * 把 indexedDB 的help 做成插件的形式
 */
exportdefault {
  _indexedDBFlag: Symbol('nf-indexedDB-help'),
  _help: {}, // 访问数据库的实例
  _store: {}, // 存放对象,实现 foo.addModel(obj)的功能 
  createHelp (info) {
    let indexedDBFlag = this._indexedDBFlag
    if (typeof info.dbFlag === 'string') {
      indexedDBFlag = Symbol.for(info.dbFlag)
    } elseif (typeof info.dbFlag === 'symbol') {
      indexedDBFlag = info.dbFlag
    }
    // 连接数据库,获得实例。
    const help = new IndexedDB(info)
    // 存入静态对象,以便于支持保存多个不同的实例。
    this._help[indexedDBFlag] = help // help
    this._store[indexedDBFlag] = {} // 仓库变对象
    // 把仓库变成对象的形式,避免写字符串的仓库名称
    for (const key in info.stores) {
      this._store[indexedDBFlag][key] = {
        put: (obj) => {
          let _id = obj
          if (typeof obj === 'object') {
            _id = obj[info.stores[key].id]
          }
          return help.updateModel(key, obj, _id)
        },
        del: (obj) => {
          let _id = obj
          if (typeof obj === 'object') {
            _id = obj[info.stores[key].id]
          }
          return help.deleteModel(key, _id)
        },
        add: (obj) => help.addModel(key, obj),
        get: (id = null) => help.getModel(key, id)
      }
    }
  },
  // 获取静态对象里的数据库实例
  useDBHelp (_dbFlag) {
    let flag = this._indexedDBFlag
    if (typeof _dbFlag === 'string') {
      flag = Symbol.for(_dbFlag)
    } elseif (typeof _dbFlag === 'symbol') {
      flag = _dbFlag
    }
    returnthis._help[flag]
  },
  useStore (_dbFlag) {
    let flag = this._indexedDBFlag
    if (typeof _dbFlag === 'string') {
      flag = Symbol.for(_dbFlag)
    } elseif (typeof _dbFlag === 'symbol') {
      flag = _dbFlag
    }
    returnthis._store[flag]
  }
}


首先,这是一个静态对象,可以存放 help 的实例,可以实现全局访问的效果。

以前是 使用 provide / inject 保存的,但是发现有点不太方便,也不是十分必要,所以改成了静态对象的方式。

然后根据建表的信息,创建仓库的对象,把字符串的仓库名称变成对象的形式,这样就方便多了。

为啥是 “useDBHelp”呢,因为要和 webSQL的 help 加以区分。

使用的时候就变成了这样:


// 把仓库当做“对象”
const  { menuMeta }  = dbInstall.useStore(dbInfo.dbFlag)
// 添加对象
const add = () => {
  const t1 = window.performance.now()
  console.log('\n -- 准备添加对象 --:', t1)
  const model = {
    id: newDate().valueOf(),
    name: 'test-。'
  }
  menuMeta.add(model).then((res) => {
    const t2 = window.performance.now()
    console.log('添加成功!', res, '用时:', t2 - t1, '\n')
  })
}

这样的话,就方便多了。对象仓库名.xxx(oo) 就可以,代码简洁了很多。


进一步套娃


上面是把对象仓库看做了“对象”,然后实现增删改查,那么能不能让object 本身实现增删改查呢?

既然封装到这一步了,我们可以再前进一下,使用 js的原型 实现 object 的增删改查。


// 给 model 加上增删改查的函数
for (const key in info.stores) {
  this._store[indexedDBFlag][key] = {
    createModel: (model) => {
      function MyModel (_model) {
        for (const key in _model) {
          this[key] = _model[key]
        }
      }
      MyModel.prototype.add = function (tran = null) {
        return help.addModel(key, this, tran)
      }
      MyModel.prototype.save = function (tran = null) {
        const _id = this[info.stores[key].id]
        return help.setModel(key, this, _id, tran)
      }
      MyModel.prototype.load = function (tran = null) {
        returnnewPromise((resolve, reject) => {
          // 套个娃
          const _id = this[info.stores[key].id]
          help.getModel(key, _id, tran).then((res) => {
            Object.assign(this, res)
            resolve(res)
          })
        })
      }
      MyModel.prototype.del = function (tran = null) {
        const _id = this[info.stores[key].id]
        return help.delModel(key, _id, tran)
      }
      const re = new MyModel(model)
      return reactive(re)
    }
  }
}


首先给对象仓库加一个 “createModel”函数,用于把 object 和对象仓库挂钩,然后用原型挂上增删改查的函数,最后 new 一个实例返回。

使用方式:


// 对象仓库,创建一个实例,reactive 形式
const testModel = menuMeta.createModel({
  id: 12345,
  name: '对象自己save'
})
// 对象直接保存
const mSave = () => {
  testModel.name = '对象自己save' + window.performance.now()
  testModel.save().then((res) => {
    // 保存完成
  })
}


因为加上了 reactive,所以可以自带响应性。这样是不是很像“充血实体类”了?

id 值建议不要修改,虽然可以改,但是总感觉改了的话比较别扭。


统一“出口”


虽然用 help 带上了几个常规操作,但是出口还是不够统一,像 Vue 那样,就一个出口是不是很方便呢?所以我们也要统一一下:

storage.js


// 引入各种函数,便于做成npm包
// indexedDB 部分
import dbHelp from'./nf-ws-indexeddb/help.js'
import dbInstall from'./nf-ws-indexeddb/install.js'
// indexedDB 部分
const dbCreateHelp = (info) => dbInstall.createHelp(info)
const useDBHelp = (_dbFlag) => dbInstall.useDBHelp(_dbFlag)
const useStores = (_dbFlag) => dbInstall.useStores(_dbFlag)
export {
  // indexedDB 部分
  dbHelp, // indexedDB 的 help
  dbCreateHelp, // 创建 help 实例,初始化设置
  useDBHelp, // 组件里获取 help 的实例
  useStores // 组件里获取对象仓库,方便实现增删改查
}

这样也便于我们打包发布到npm。


在 vue 里面使用


基本工作都作好了,就剩最后一个问题了,在 Vue3 里面如何使用呢?

我们可以仿造一下 vuex 的使用方式,先建立一个 js文件,实现统一设置。

store-project/db.js


// 引入 indexedDB 的 help
import { dbCreateHelp } from'../../packages/storage.js'
// 引入数据库数据
const db = {
  dbName: 'nf-project-meta',
  ver: 5
}
/**
 * 设置
 */
exportdefaultfunction setup (callback) {
  const install = dbCreateHelp({
    // dbFlag: 'project-meta-db',
    dbConfig: db,
    stores: { // 数据库里的表
      moduleMeta: { // 模块的meta {按钮,列表,分页,查询,表单若干}
        id: 'moduleId',
        index: {},
        isClear: false
      },
      menuMeta: { // 菜单用的meta
        id: 'id',
        index: {},
        isClear: false
      },
      serviceMeta: { // 后端API的meta,在线演示用。
        id: 'moduleId',
        index: {},
        isClear: false
      },
      testIndex: { // 测试索引和查询。
        id: 'moduleId',
        index: {
          kind: false,
          type: false
        },
        isClear: false
      }
    },
    // 加入初始数据
    init (help) {
      if (typeof callback === 'function') {
        callback(help)
      }
    }
  })
  return install
}


然后在 main.js 里面调用,因为这是最早执行代码的地方,可以第一时间建立数据库。


// 引入 indexedDB 的help
import dbHelp from'./store-project/db.js'
dbHelp((help) => {
  // indexedDB 准备好了
  console.log('main里面获取 indexedDB 的help', help)
})


同时可以把 help 的实例存入静态对象里面。

其实一开始是使用 provide 注入的,但是发现不是太适合,因为在main.js这个层级里面无法使用inject读取出来,这样的话,和状态等的操作就不太方便。

所以干脆放在静态对象里面好了,任何地方都可以访问到。

并不需要使用 use 挂载到 App 上面。


索引和查询


由于篇幅有限,这里就先不介绍了,如果大家感兴趣的话,可以在写一篇补充一下。


源码


封装前端存储https://gitee.com/naturefw/nf-web-storage


在线演示


https://naturefw.gitee.io/vite2-vue3-demo

安装方式

yarn add nf-web-storage

本文作者:自然框架

个人网址:jyk.cnblogs.com

声明:本文为 脚本之家专栏作者 投稿,未经允许请勿转载。

相关文章
|
1月前
|
前端开发 JavaScript UED
深入了解JavaScript异步编程:回调、Promise与async/await
【10月更文挑战第11天】深入了解JavaScript异步编程:回调、Promise与async/await
15 0
|
2月前
|
前端开发 JavaScript
解决异步问题,教你如何写出优雅的promise和async/await,告别callback回调地狱!
该文章教授了如何使用Promise和async/await来解决异步编程问题,从而避免回调地狱,使代码更加清晰和易于管理。
解决异步问题,教你如何写出优雅的promise和async/await,告别callback回调地狱!
|
5月前
|
存储 前端开发 JavaScript
中间件回调和Promise
【6月更文挑战第18天】
36 1
|
6月前
|
前端开发 JavaScript
js开发:请解释Promise是什么,以及它如何解决回调地狱(callback hell)问题。
Promise是JavaScript解决异步操作回调地狱的工具,代表未来可能完成的值。传统的回调函数嵌套导致代码难以维护,而Promise通过链式调用`.then()`和`.catch()`使异步流程清晰扁平。每个异步操作封装为Promise,成功时`.then()`传递结果,出错时`.catch()`捕获异常。ES6的`async/await`进一步简化Promise的使用,使异步代码更接近同步风格。
91 1
|
6月前
|
前端开发 JavaScript
vue组件制作专题 - (mpvue专用)在mpvue中手写css实现简单左右轮播
vue组件制作专题 - (mpvue专用)在mpvue中手写css实现简单左右轮播
49 0
|
6月前
|
前端开发 API
用promise封装ajax
用promise封装ajax
46 0
|
6月前
|
前端开发 JavaScript
如何处理 JavaScript 中的异步操作和 Promise?
如何处理 JavaScript 中的异步操作和 Promise?
65 1
|
6月前
|
前端开发 JavaScript
在JavaScript中,什么是promise、怎么使用promise、怎么手写promise
在JavaScript中,什么是promise、怎么使用promise、怎么手写promise
99 4
|
6月前
|
前端开发 JavaScript 开发者
JavaScript 中的异步编程:Promise 和 Async/Await
在现代的 JavaScript 开发中,异步编程是至关重要的。本文将介绍 JavaScript 中的异步编程概念,重点讨论 Promise 和 Async/Await 这两种常见的处理异步操作的方法。通过本文的阐述,读者将能够更好地理解和应用这些技术,提高自己在 JavaScript 开发中处理异步任务的能力。
|
5月前
|
前端开发 JavaScript 开发者
JavaScript进阶-Promise与异步编程
【6月更文挑战第20天】JavaScript的Promise简化了异步操作,从ES6开始成为标准。Promise有三种状态:pending、fulfilled和rejected。基本用法涉及构造函数和`.then`处理结果,如: ```javascript new Promise((resolve, reject) => { setTimeout(resolve, 2000, '成功'); }).then(console.log); // 输出: 成功
89 4
下一篇
无影云桌面