浏览器数据库 IndexedDB学习

简介

IndexedDB是HTML5规范里新出现的浏览器里内置的数据库。对于在浏览器里存储数据,你可以使用cookies或local storage,但它们都是比较简单的技术,而IndexedDB提供了类似数据库风格的数据存储和使用方式。存储在IndexedDB里的数据是永久保存,不像cookies那样只是临时的。IndexedDB里提供了查询数据的功能,在online和offline模式下都能使用。你可以用IndexedDB存储大型数据。

IndexedDB里数据以对象的形式存储,每个对象都有一个key值索引。IndexedDB里的操作都是事务性的。一种对象存储在一个objectStore里,objectStore就相当于关系数据库里的表。IndexedDB可以有很多objectStore,objectStore里可以有很多对象。每个对象可以用key值获取。

IndexedDB vs LocalStorage

IndexedDB和LocalStorage都是用来在浏览器里存储数据,但它们使用不同的技术,有不同的用途,你需要根据自己的情况适当的选择使用哪种。LocalStorage是用key-value键值模式存储数据,但跟IndexedDB不一样的是,它的数据并不是按对象形式存储。它存储的数据都是字符串形式。如果你想让LocalStorage存储对象,你需要借助JSON.stringify()能将对象变成字符串形式,再用JSON.parse()将字符串还原成对象。但如果要存储大量的复杂的数据,这并不是一种很好的方案。毕竟,localstorage就是专门为小数量数据设计的,它的api是同步的。

IndexedDB很适合存储大量数据,它的API是异步调用的。IndexedDB使用索引存储数据,各种数据库操作放在事务中执行。IndexedDB甚至还支持简单的数据类型。IndexedDB比localstorage强大得多,但它的API也相对复杂。

对于简单的数据,你应该继续使用localstorage,但当你希望存储大量数据时,IndexedDB会明显的更适合,IndexedDB能提供你更为复杂的查询数据的方式。

IndexedDB vs Web SQL

WebSQL也是一种在浏览器里存储数据的技术,跟IndexedDB不同的是,IndexedDB更像是一个NoSQL数据库,而WebSQL更像是关系型数据库,使用SQL查询数据。W3C已经不再支持这种技术。

具体情况请看:www.w3.org/TR/webdatab…

因为不再支持,所以你就不要在项目中使用这种技术了。

IndexedDB vs Cookies

每次HTTP接受和发送都会传递Cookies数据,它会占用额外的流量。例如,如果你有一个10KB的Cookies数据,发送10次请求,那么,总计就会有100KB的数据在网络上传输。Cookies只能是字符串。浏览器里存储Cookies的空间有限,很多用户禁止浏览器使用Cookies。所以,Cookies只能用来存储小量的非关键的数据。

IndexedDB的浏览器支持

不同的浏览器对于IndexedDB有不同的实现,正常来说,我们可以使用window.indexedDB来获取到浏览器的indexedDB对象。但是对于某些浏览器来说,还没有使用标准的window.indexedDB,而是用带前缀的实现。

所以我们在使用过程中通常需要进行判断和转换:

// In the following line, you should include the prefixes of implementations you want to test.
window.indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB;
// DON'T use "var indexedDB = ..." if you're not in a function.
// Moreover, you may need references to some window.IDB* objects:
window.IDBTransaction = window.IDBTransaction || window.webkitIDBTransaction || window.msIDBTransaction || {READ_WRITE: "readwrite"}; // This line should only be needed if it is needed to support the object's constants for older browsers
window.IDBKeyRange = window.IDBKeyRange || window.webkitIDBKeyRange || window.msIDBKeyRange;
// (Mozilla has never prefixed these objects, so we don't need window.mozIDB*)
复制代码

上面我们从window获取了indexedDB,IDBTransaction和IDBKeyRange三个对象。

其中indexedDB表示的是数据库的连接。IDBTransaction表示的是transaction,而IDBKeyRange则是用从数据库的某个特定key range中取出数据。

但是,通常来说带前缀的实现一般都是不稳定的,所以我们通常不建议在正式环境中使用,所以如果不支持标准表达式的话,需要直接报错:

if (!window.indexedDB) {
    console.log("Your browser doesn't support a stable version of IndexedDB. Such and such feature will not be available.");
}
复制代码

阮一峰 : 浏览器数据库 IndexedDB 入门教程

indexDB的理解与使用

使用场景

结合它的优、缺点谈谈其使用场景。

  • 不适合过大的数据存储,浏览器对 indexDB 有 50M 大小的限制
  • 不适合对兼容性要求高的项目
  • 不适合存储敏感数据
  • 当用户清除浏览器缓存的时候可能出现问题
  • indexedDB 受到同源策略的限制

工具方法

/**
 * 封装的方法以及用法
    <!-- <script type="module">
    import { openDB,
            addData,
            getDataByKey,
            cursorGetData,
            getDataByIndex,
            cursorGetDataByIndex,
            updateDB,
            deleteDB,
            cursorDelete,
            closeDB,
            deleteDBAll } from './indexedDb.js'
    openDB('myDB', 'one').then(db => {  // 打开 / 创建 Indexed 数据 
      // addData(db, 'one', { id: 1, name: '张三', age: 24 }) // 新增数据
      // addData(db, 'one', { id: 2, name: '李四', age: 30 }) // 新增数据
      // getDataByKey(db, 'one', 1) // 通过主键读取数据
      // cursorGetData(db, 'one') // 通过游标读取数据
      // getDataByIndex(db, 'one','name', '张三') // 通过索引读取数据
      // cursorGetDataByIndex(db, 'one', 'name', '张三') // 通过索引和游标查询记录
      // updateDB(db, 'one', { id: 1, name: '张三', age: 25 }) // 更新数据
      // deleteDB(db, 'one', 2) // 删除数据
      // cursorDelete(db, 'one', 'name', '张三') // 通过索引和游标删除指定的数据
      // closeDB(db) // 关闭数据库
    })
    // deleteDBAll('myDB') // 删除数据库 慎用!!!!!!!!!!
  </script> -->
 * 
 * 打开数据库
 * @param {object} dbName 数据库的名字
 * @param {string} storeName 仓库名称
 * @param {string} version 数据库的版本
 * @return {object} 该函数会返回一个数据库实例
 */
export function openDB(dbName, storeName, version = 1) {
  return new Promise((resolve, reject) => {
    //  兼容浏览器
    var indexedDB =
      window.indexedDB ||
      window.mozIndexedDB ||
      window.webkitIndexedDB ||
      window.msIndexedDB
    let db
    const request = indexedDB.open(dbName, version)
    request.onsuccess = function (event) {
      db = event.target.result // 数据库对象
      console.log('数据库打开成功')
      resolve(db)
    }

    request.onerror = function (event) {
      console.log('数据库打开报错')
    }

    request.onupgradeneeded = function (event) {
      // 数据库创建或升级的时候会触发
      console.log('onupgradeneeded')
      db = event.target.result // 数据库对象
      var objectStore
      if (!db.objectStoreNames.contains(storeName)) {
        objectStore = db.createObjectStore(storeName, { keyPath: 'id' }) // 创建表
        objectStore.createIndex('name', 'name', { unique: false }) // 创建索引 可以让你搜索任意字段
        // objectStore.createIndex('address', 'address', { unique: false })
        // objectStore.createIndex('nameAddr', ['name', 'address'], {
        //  unique: false,
        // })
        // objectStore.createIndex('flag', 'flag', { unique: false })
      }
    }
  })
}

/**
 * 新增数据
 * @param {object} db 数据库实例
 * @param {string} storeName 仓库名称
 * @param {string} data 数据
 */
export function addData(db, storeName, data) {
  var request = db
    .transaction([storeName], 'readwrite') // 事务对象 指定表格名称和操作模式("只读"或"读写")
    .objectStore(storeName) // 仓库对象
    .add(data)

  request.onsuccess = function (event) {
    console.log('数据写入成功')
  }

  request.onerror = function (event) {
    console.log('数据写入失败')
    throw new Error(event.target.error)
  }
}

/**
 * 通过主键读取数据
 * @param {object} db 数据库实例
 * @param {string} storeName 仓库名称
 * @param {string} key 主键值
 */
export function getDataByKey(db, storeName, key) {
  return new Promise((resolve, reject) => {
    var transaction = db.transaction([storeName]) // 事务
    var objectStore = transaction.objectStore(storeName) // 仓库对象
    var request = objectStore.get(key)

    request.onerror = function (event) {
      console.log('事务失败')
    }

    request.onsuccess = function (event) {
      console.log('主键查询结果: ', request.result)
      resolve(request.result)
    }
  })
}

/**
 * 通过游标读取数据
 * @param {object} db 数据库实例
 * @param {string} storeName 仓库名称
 */
export function cursorGetData(db, storeName) {
  let list = []
  var store = db
    .transaction(storeName, 'readwrite') // 事务
    .objectStore(storeName) // 仓库对象
  var request = store.openCursor() // 指针对象
  request.onsuccess = function (e) {
    var cursor = e.target.result
    if (cursor) {
      // 必须要检查
      list.push(cursor.value)
      cursor.continue() // 遍历了存储对象中的所有内容
    } else {
      console.log('游标查询结果:', list)
    }
  }
}

/**
 * 通过索引读取数据
 * @param {object} db 数据库实例
 * @param {string} storeName 仓库名称
 * @param {string} indexName 索引名称
 * @param {string} indexValue 索引值
 */
export function getDataByIndex(db, storeName, indexName, indexValue) {
  var store = db.transaction(storeName, 'readwrite').objectStore(storeName)
  var request = store.index(indexName).get(indexValue)
  request.onerror = function () {
    console.log('事务失败')
  }
  request.onsuccess = function (e) {
    var result = e.target.result
    console.log('索引查询结果:', result)
  }
}

/**
 * 通过索引和游标查询记录
 * @param {object} db 数据库实例
 * @param {string} storeName 仓库名称
 * @param {string} indexName 索引名称
 * @param {string} indexValue 索引值
 */
export function cursorGetDataByIndex(db, storeName, indexName, indexValue) {
  let list = []
  var store = db.transaction(storeName, 'readwrite').objectStore(storeName) // 仓库对象
  var request = store
    .index(indexName) // 索引对象
    .openCursor(IDBKeyRange.only(indexValue)) // 指针对象
  request.onsuccess = function (e) {
    var cursor = e.target.result
    if (cursor) {
      // 必须要检查
      list.push(cursor.value)
      cursor.continue() // 遍历了存储对象中的所有内容
    } else {
      console.log('游标索引查询结果:', list)
    }
  }
  request.onerror = function (e) {}
}

/**
 * 更新数据
 * @param {object} db 数据库实例
 * @param {string} storeName 仓库名称
 * @param {object} data 数据
 */
export function updateDB(db, storeName, data) {
  var request = db
    .transaction([storeName], 'readwrite') // 事务对象
    .objectStore(storeName) // 仓库对象
    .put(data)

  request.onsuccess = function () {
    console.log('数据更新成功')
  }

  request.onerror = function () {
    console.log('数据更新失败')
  }
}

/**
 * 删除数据
 * @param {object} db 数据库实例
 * @param {string} storeName 仓库名称
 * @param {object} id 主键值
 */
export function deleteDB(db, storeName, id) {
  var request = db
    .transaction([storeName], 'readwrite')
    .objectStore(storeName)
    .delete(id)

  request.onsuccess = function () {
    console.log('数据删除成功')
  }

  request.onerror = function () {
    console.log('数据删除失败')
  }
}

/**
 * 通过索引和游标删除指定的数据
 * @param {object} db 数据库实例
 * @param {string} storeName 仓库名称
 * @param {string} indexName 索引名
 * @param {object} indexValue 索引值
 */
export function cursorDelete(db, storeName, indexName, indexValue) {
  var store = db.transaction(storeName, 'readwrite').objectStore(storeName)
  var request = store
    .index(indexName) // 索引对象
    .openCursor(IDBKeyRange.only(indexValue)) // 指针对象
  request.onsuccess = function (e) {
    var cursor = e.target.result
    var deleteRequest
    if (cursor) {
      deleteRequest = cursor.delete() // 请求删除当前项
      deleteRequest.onerror = function () {
        console.log('游标删除该记录失败')
      }
      deleteRequest.onsuccess = function () {
        console.log('游标删除该记录成功')
      }
      cursor.continue()
    }
  }
  request.onerror = function (e) {}
}

/**
 * 关闭数据库
 * @param {object} db 数据库实例
 */
export function closeDB(db) {
  db.close()
  console.log('数据库已关闭')
}

/**
 * 删除数据库
 * @param {object} dbName 数据库名称
 */
export function deleteDBAll(dbName) {
  console.log(dbName)
  let deleteRequest = window.indexedDB.deleteDatabase(dbName)
  deleteRequest.onerror = function (event) {
    console.log('删除失败')
  }
  deleteRequest.onsuccess = function (event) {
    console.log('删除成功')
  }
}
复制代码
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享