【JavaScript】前端存储(二) —— IndexedDB

〇、为什么使用IndexedDB

​ 最近遇到一个需求,一个页面要上传至少五六十张图片,同时支持页面内回显,但是后台的图片服务是一个异步的过程,图片上传以后可能10分钟后才能查回来,可页面又要上传后能够立即回显查看,那就只能存储在前端了,也就是说需要在一个页面中存储至少几十张图片。
众但前端页面存储内容过多后可能直接导致页面崩溃,接下来想到了存在本地缓存中,但是常用的sessionStoragelocalStorage一般只能存储5M的数据,这就又有一个问题,现在的手机拍的图片体积都比较大,尤其是转成base64后体积又会增长大约30%,即使采用压缩都压缩到100K,最多也就能存50张图片,况且也不可能压缩到那么小,否则就成马赛克了,这肯定是无法满足几十张图片的存储了。
在查询资料的过程中发现了IndexedDB数据库,IndexedDB是一种浏览器数据库,储存空间大,一般不少于 250MB,甚至部分浏览器没有上限,以下是关于IndexedDB的一些介绍和我使用IndexedDB时的使用方法和一些坑。

一、IndexedDB的概念及特点

1、概念(详情参考MDN

  1. IndexedDB 是一种底层 API,用于在客户端存储大量的结构化数据(也包括文件/二进制大型对象(blobs))。

2、特点(详情参考MDN

  1. 键值对储存: IndexedDB 内部采用对象仓库(object store)存放数据。所有类型的数据都可以直接存入,包括 JavaScript 对象。对象仓库中,数据以“键值对”的形式保存,每一个数据记录都有对应的主键,主键是独一无二的,不能有重复,否则会抛出一个错误。
  2. 异步: IndexedDB 操作时不会锁死浏览器,用户依然可以进行其他操作,这与 LocalStorage 形成对比,后者的操作是同步的。异步设计是为了防止大量数据的读写,拖慢网页的表现。
  3. 支持事务: IndexedDB 支持事务(transaction),这意味着一系列操作步骤之中,只要有一步失败,整个事务就都取消,数据库回滚到事务发生之前的状态,不存在只改写一部分数据的情况。
  4. 同源限制: IndexedDB 受到同源限制,每一个数据库对应创建它的域名。网页只能访问自身域名下的数据库,而不能访问跨域的数据库。
  5. 储存空间大: IndexedDB 的储存空间比 LocalStorage 大得多,一般来说不少于 250MB,甚至没有上限。
  6. 支持二进制储存: IndexedDB 不仅可以储存字符串,还可以储存二进制数据(ArrayBuffer 对象和 Blob 对象)。

二、基本使用

我是在vue项目中使用的,就直接封装了一个方法,包含初始化数据库、关闭数据库、新增数据、读取数据、清空数据库等几个方法。

1、封装indexDB.JS

  1. 声明indexDB

    const indexDB = window.indexedDB || window.webkitIndexedDB || window.mozIndexedDB
    
  2. 创建构造函数

    class IndexDBCache {
      // 构造函数
      constructor() {
        this._db = null //数据库
        this._transaction = null //事务
        this._request = null
        this._dbName = 'test' //数据库名
        this._cacheTableName = 'imageCache' //表名
        this._dbversion = 1 //数据库版本 
      }
    }
    export default IndexDBCache
    

    这里只针对当前这个需求,所以数据库名和表名都直接写死了。

  3. 初始化数据库

    // 初始化数据库
    initDB() {
        return new Promise((resolve, reject) => {
            this._request = indexDB.open(this._dbName, this._dbversion) // 打开数据库
            // 数据库初始化成功
            this._request.onsuccess = (event) => {
                this._db = this._request.result
                resolve(event)
            }
            // 数据库初始化失败
            this._request.onerror = (event) => {
                reject(event)
            }
            // 数据库初次创建或更新时会触发
            this._request.onupgradeneeded = (event) => {
                let db = this._request.result
                if (!db.objectStoreNames.contains(this._cacheTableName)) {
                    db.createObjectStore(this._cacheTableName, {
                        keyPath: 'imageName', // 设置主键
                    })
                }
                resolve(event)
            }
        })
    }
    

    主键名就是图片的名称,这里直接写死了,主键必须是唯一的不允许重复,也可以设置索引,数据量特别大时,效率会更高,不过我这里不需要,就没有设置索引。

  4. 关闭数据库

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

    关闭数据库的方法,在数据库使用完后要关闭。

  5. 新增数据的方法

      /**
       * @description : 新增数据
       * @param        {Object} params 添加到数据库中的数据 { imageName: 文件名, image: base64格式图片 }
       * @return       {*}
       */
      addData(params) {
        return new Promise((resolve, reject) => {
          let transaction = this._db.transaction(this._cacheTableName, 'readwrite')
          let store = transaction.objectStore(this._cacheTableName)
          let response = store.add(params)
          // 操作成功
          response.onsuccess = (event) => {
            resolve(event)
          }
          // 操作失败
          response.onerror = (event) => {
            reject(event)
          }
        })
      }
    

    注意:indexedDB都是异步操作,具体操作可在回调函数中写

  6. 读取数据的方法

    // 通过主键读取数据
    getDataByKey(key) {
        return new Promise((resolve, reject) => {
            let transaction = this._db.transaction(this._cacheTableName)
            let objectStore = transaction.objectStore(this._cacheTableName)
            // 通过主键读取数据
            let request = objectStore.get(key)
            // 操作成功
            request.onsuccess = () => {
                resolve(request.result)
            }
            // 操作失败
            request.onerror = (event) => {
                reject(event)
            }
        })
    }
    

    我这里读取数据就直接根据主键来获取数据库中的数据,如果初始化数据库时设置了索引,也可以根据索引来获取,数据量大时,效率应该更高,不过我这里只有几十个图片的数据,就无所谓了。

  7. 清空数据库数据

    // 清空数据库数据
    clearDB() {
        return new Promise((resolve, reject) => {
            let transaction = this._db.transaction(this._cacheTableName, 'readwrite')
            let store = transaction.objectStore(this._cacheTableName)
            let response = store.clear()
            // 操作成功
            response.onsuccss = (event) => {
                resolve(event)
            }
            // 操作失败
            response.onerror = (event) => {
                reject(event)
            }
        })
    }
    

    在不需要这些数据时,要把数据库的数据删除掉,否则会一直存储在浏览器中。

2、组件中使用

  1. 引入并初始化数据建库

    import IndexDBCache from '../../utils/indexDB'
    let imageDB = null
    
    //初始化数据库
    initIndexDB () {
        imageDB = new IndexDBCache()
        imageDB.initDB().then(res => {
            if (res.type == 'upgradeneeded') {
                console.log('indexDB 数据库创建或更新成功!')
            } else {
                console.log('indexDB 数据库初始化成功!')
            }
        }).catch((err) => {
            console.log('indexDB 数据库初始化失败! ', err)
        })
    }
    

    执行完数据库的初始化后,在浏览器的调试工具中可以看到新建的名为test的数据库和表名为imageCache的数据表,同时可以看到表结构为key:value的形式,主键为imageName

在这里插入图片描述

  1. 写入数据库

    // 写入数据库
    setData (imageName, imageData) {
        let data = { imageName,  imageData }
        imageDB.addData(data).then((res) =>{
            console.log('写入 indexDB 数据库成功', res)
        }).catch((err) =>{
            console.log('写入 indexDB 数据库失败==>', err)
        })
    },
    

    写入数据库数据后,可以刷新后直接查看刚写入的数据,可以看到key就是设置的imageNamevalue则是存入的数据对象。

在这里插入图片描述

  1. 从数据库读取数据

    //从数据库获取数据
    getImageByName (imageName) {
        imageDB.getDataByKey(imageName).then((res) =>{
            console.log('从indexDB数据库获取数据成功', res)
        }).catch((err) =>{
            console.log('从indexDB数据库获取数据失败==>', err)
        })
    },
    

    根据主键从数据库中获取数据,从控制台可以直接打印出取出的数据。

在这里插入图片描述

  1. 清空数据库的操作

    // 清空数据库的数据
    clearIndexDB () {
        imageDB.clearDB()
    }
    

    在离开页面时触发清空数据库数据的方法,清空数据库后可以看到表中的数据已经没有了,
    在这里插入图片描述

三、踩到的坑

  1. 无法删除数据库

    ​ 在上面的使用方法中可以看到,我最后离开页面时使用了清空数据库的方法,为什么不直接删除掉数据库呢?因为我删不掉,下面是我使用的方法

    indexDB.js中封装的删除数据库的方法

    // 删除数据库
    deleteDB() {
        console.log('开始删除数据库')
        let DBDeleteRequest = indexedDB.deleteDatabase(this._dbName)
        DBDeleteRequest.onsuccess = function (event) {
            console.log('删除数据库成功')
        }
        DBDeleteRequest.onerror = function (event) {
            console.log('删除数据库失败')
        }
    }
    

    组件中调用

    // 删除数据库
    delIndexDB () {
        console.log('删除数据库')
        imageDB.deleteDB()
    }
    

    执行结果,在执行删除操作后,可以看到控制台只打印了删除数据库开始删除数据库,后面的删除数据库成功删除数据库失败根本没有打印出来,查看数据库,发现数据库和表中的数据都在,从页面上获取数据,也能够成功获取到,证明数据库没有删除成功,为了在离开页面后清空数据,但又删不掉数据库,所以采取了离开当前页面清空数据库的方式来删除数据,如果您有更好的方法,欢迎一起探讨。
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

  • 9
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

马大头0

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值