提及 IndexedDB,你们应该会有一些疑问,好比:什么是 IndexedDB?适合什么业务场景?哪些公司哪些业务已经开始使用 indexedDB了?带着这些问题,阅读本文,相信可以给你答案。html
在开始以前,咱们先简单梳理一下浏览器存储的几种方式(详见👉浏览器存储方式)vue
会话期 Cookie | 持久性 Cookie | sessionStorage | localStorage | indexedDB | WebSQL | |
---|---|---|---|---|---|---|
存储大小 | 4kb | 4kb | 2.5~10 MB | 2.5~10 MB | >250MB | 已废弃 |
失效时间 | 浏览器关闭自动清除 | 设置过时时间,到期后清除 | 浏览器关闭后清除 | 永久保存(除非手动清除) | 手动更新或删除 | 已废弃 |
与服务端交互 | 有 | 有 | 无 | 无 | 无 | 已废弃 |
访问策略 | 符合同源策略能够访问 | 符合同源策略能够访问 | 符合同源策略能够访问 | 即便同源也不可相互访问 | 符合同源策略能够访问 | 已废弃 |
WebSQL 是一种浏览器存储方案,属于传统的关系型数据库,须要写 sql 语句查询。WebSQL 出现过一段时间,虽然已被部分浏览器支持,但又被废弃,由 IndexedDB 取代。
废弃缘由:
This document was on the W3C Recommendation track but specification work has stopped. The specification reached an impasse: all interested implementors have used the same SQL backend (Sqlite), but we need multiple independent implementations to proceed along a standardisation path.
大意:
该文件是W3C推荐标准,但规范的制定工做已经中止。该规范陷入僵局:全部感兴趣的实现者都使用了相同的SQL后端(SQLite的),但咱们须要多个独立的实现沿着规范化的路径进行。web
cookie 和 webStorage 存储数据格式仅支持 String,存储时须要借助 JSON.stringify()
将 JSON 对象转化为字符串,读取时须要借助 JSON.parse()
将字符串转化为 JSON 对象。sql
通常来讲,咱们更推荐使用 webStroage,但其存储大小有限、数据存储仅支持 String 格式、不提供搜索功能,不能创建自定义的索引。所以,须要一种新的解决方案,这就是 IndexedDB
诞生的背景。vuex
通俗地说,IndexedDB 就是浏览器提供的本地数据库,它能够被网页脚本建立和操做。IndexedDB 容许储存大量数据,提供查找接口,还能创建索引。这些都是 LocalStorage 所不具有的。就数据库类型而言,IndexedDB 不属于关系型数据库(不支持 SQL 查询语句),更接近 NoSQL 数据库。
从 DB(Data Base) 能够看出它是一个数据库。经常使用的数据库有两种类型:数据库
MySQL
、Oracle
、WebSQL
(已废弃)Redis
、MongoDB
、IndexedDB
IndexedDB 是非关系型数据库,不须要写 sql 语句进行数据库操做,数据格式可以使用 JSON 对象。segmentfault
IndexedDB 有不少优势:后端
存储格式多样:跨域
对象仓库 (object store)
对于简单的数据,应该继续使用 localStorage;对于大量结构化数据,indexedDB 会更适合。固然若是你须要设置过时时间的短时间存储,仍是使用 cookie 存储吧。浏览器
下面给出了三种使用方案,你能够用最简单的原生API 进行基本操做,也能够本身封装一个库,也能够用第三方库。
(1)基础调用
(2)本身封装
<script> var myDB = { name: 'school', // 数据库名 version: 1, // 数据库版本号 db: null, ojstore: { name: 'teachers', // 存储空间表的名字 keypath: 'id', // 主键 indexKey: 'age' // 年龄索引 } } var INDEXDB = { indexedDB: window.indexedDB || window.webkitindexedDB, IDBKeyRange: window.IDBKeyRange || window.webkitIDBKeyRange, // 键范围 // 打开或建立数据库,创建对象存储空间 ObjectStore openDB: function (dbname, dbversion) { var that = this var version = dbversion || 1 var request = that.indexedDB.open(dbname, version) request.onerror = function (e) { console.log(e.currentTarget.error.message) } request.onsuccess = function (e) { myDB.db = e.target.result console.log('成功创建并打开数据库:' + myDB.name + 'version' + dbversion) } request.onupgradeneeded = function (e) { var db = e.target.result var transaction = e.target.transaction var store if (!db.objectStoreNames.contains(myDB.ojstore.name)) { //没有该对象空间时建立该对象空间 store = db.createObjectStore(myDB.ojstore.name, { keyPath: myDB.ojstore.keypath }) console.log('成功创建对象存储空间:' + myDB.ojstore.name) that.storeIndex(store, myDB.ojstore.indexKey) } } }, // 删除数据库 deletedb: function (dbname) { var that = this that.indexedDB.deleteDatabase(dbname) console.log(dbname + '数据库已删除') }, // 关闭数据库 closeDB: function (db) { db.close() console.log('数据库已关闭') }, // 添加数据,重复添加会报错 addData: function (db, storename, data) { var store = db.transaction(storename, 'readwrite').objectStore(storename) var request for(var i = 0; i < data.length; i++) { request = store.add(data[i]) request.onerror = function() { console.error('add添加数据库中已有该数据') } request.onsuccess = function() { console.log('add添加数据已存入数据库') } } }, // 经过游标查询记录 cursorGetData: function (db, storename, keyRange) { var keyRange = keyRange || '' var store = db.transaction(storename, 'readwrite').objectStore(storename) var request = store.openCursor(keyRange) request.onsuccess = function (e) { var cursor = e.target.result if (cursor) { // 必需要检查 console.log(cursor) cursor.continue() // 遍历了存储对象中的全部内容 } else{ console.log('游标查询结束') } } }, // 经过索引游标查询记录 cursorGetDataByIndex: function (db, storename, keyRange) { var keyRange = keyRange || '' var store = db.transaction(storename, 'readwrite').objectStore(storename) var request = store.index('age').openCursor(keyRange) request.onsuccess = function (e) { console.log('游标开始查询') var cursor = e.target.result if (cursor) {//必需要检查 console.log(cursor) cursor.continue()//遍历了存储对象中的全部内容 } else { console.log('游标查询结束') } } }, // 经过游标更新记录 cursorUpdateData: function (db, storename) { var keyRange = keyRange || '' var store = db.transaction(storename,'readwrite').objectStore(storename) var request = store.openCursor() request.onsuccess = function (e) { console.log('游标开始查询') var cursor = e.target.result var value, updateRequest if (cursor) { // 必需要检查 console.log(cursor) if (cursor.key === 1002) { console.log('游标开始更新') value = cursor.value value.age = 38 updateRequest = cursor.update(value) updateRequest.onerror = function () { console.log('游标更新失败') } updateRequest.onsuccess = function () { console.log('游标更新成功') } } else { cursor.continue() } } } }, // 经过游标删除记录 cursorDeleteData: function (db, storename) { var keyRange = keyRange || '' var store = db.transaction(storename, 'readwrite').objectStore(storename) var request = store.openCursor() request.onsuccess = function (e) { var cursor = e.target.result var value, deleteRequest if (cursor) { if (cursor.key === 1003) { deleteRequest = cursor.delete() // 请求删除当前项 deleteRequest.onerror = function () { console.log('游标删除该记录失败') } deleteRequest.onsuccess = function () { console.log('游标删除该记录成功') } } else { cursor.continue() } } } }, // 建立索引 storeIndex: function (store, indexKey) { var index = store.createIndex(indexKey, indexKey, { unique:false }) console.log('建立索引' + indexKey + '成功') } } var teachers = [{ id:1001, name:'Byron', age:21 }, { id:1002, name:'Frank', age:22 }, { id:1003, name:'Aaron', age:23 }, { id:1004, name:'Aaron', age:24 }, { id:1005, name:'Byron', age:24 }, { id:1006, name:'Frank', age:30 }, { id:1007, name:'Aaron', age:26 }, { id:1008, name:'Aaron', age:27 }] INDEXDB.openDB(myDB.name, myDB.version) setTimeout(function() { // 添加数据 INDEXDB.addData(myDB.db, myDB.ojstore.name, teachers) // 游标更新数据id1002更新其age为38 INDEXDB.cursorUpdateData(myDB.db, myDB.ojstore.name) // 游标删除id为1003的数据 // INDEXDB.cursorDeleteData(myDB.db, myDB.ojstore.name) // 关闭数据库 // INDEXDB.closeDB(myDB.db) // 删除数据库 // INDEXDB.deletedb(myDB.db) /* *游标键范围方法调用 */ var IDBKeyRange = window.IDBKeyRange || window.webkitIDBKeyRange // 查找1004对象 // var onlyKeyRange = IDBKeyRange.only(1004) // INDEXDB.cursorGetData(myDB.db, myDB.ojstore.name, onlyKeyRange) // 查找从1004对象开始 // var lowerBoundKeyRange = IDBKeyRange.lowerBound(1004) // INDEXDB.cursorGetData(myDB.db, myDB.ojstore.name, lowerBoundKeyRange) // 查找从1004对象开始不包括1004 // var lowerBoundKeyRangeTrue = IDBKeyRange.lowerBound(1004, true) // INDEXDB.cursorGetData(myDB.db, myDB.ojstore.name, lowerBoundKeyRangeTrue) // 查找到1004对象结束 // var upperBoundKeyRange = IDBKeyRange.upperBound(1004) // INDEXDB.cursorGetData(myDB.db, myDB.ojstore.name, upperBoundKeyRange) // 查找到1004对象结束不包括1004 // var upperBoundKeyRangeTrue = IDBKeyRange.upperBound(1004, true) // INDEXDB.cursorGetData(myDB.db, myDB.ojstore.name, upperBoundKeyRangeTrue) // 查找到1002到1004对象 // var boundKeyRange = IDBKeyRange.bound(1002, 1004) // INDEXDB.cursorGetData(myDB.db, myDB.ojstore.name, boundKeyRange) // 查找到1002到1004对象不包括1002 // var boundKeyRangeLowerTrue = IDBKeyRange.bound(1002, 1004, true) // INDEXDB.cursorGetData(myDB.db, myDB.ojstore.name, boundKeyRangeLowerTrue) // 查找到1002到1004对象包括1002不包括1004 // var boundKeyRangeUpperTrue = IDBKeyRange.bound(1002, 1004, false, true) // INDEXDB.cursorGetData(myDB.db, myDB.ojstore.name, boundKeyRangeUpperTrue) // 查找到1002到1004对象不包括1002不包括1004 // var boundKeyRangeLTUT = IDBKeyRange.bound(1002, 1004, true, true) // INDEXDB.cursorGetData(myDB.db, myDB.ojstore.name, boundKeyRangeLTUT) /* *存储键游标查询与索引键游标查询对比 */ // 存储键游标查询 // var onlyKeyRange = IDBKeyRange.only(1004) // INDEXDB.cursorGetData(myDB.db, myDB.ojstore.name, onlyKeyRange) // 索引键游标查询 // var onlyKeyRange = IDBKeyRange.only(30) // INDEXDB.cursorGetDataByIndex(myDB.db, myDB.ojstore.name, onlyKeyRange) }, 1000) </script>
(3)第三方库:Dexie.js
Dexie.js 是 indexedDB 封装 SDK,API 简洁强大、错误处理简单而强壮。
官方文档: https://dexie.org/
<!doctype html> <html> <head> <!-- Include Dexie --> <script src="https://unpkg.com/dexie@latest/dist/dexie.js"></script> <script> // Define your database var db = new Dexie("student_database"); db.version(1).stores({ students: 'name' }); // // Put some data into it // var data = { name: "Byron", shoeSize: 24 } db.students.put(data).then (function (){ // // Then when data is stored, read from it // return db.students.get('Nicolas'); }).then(function (student) { // // Display the result // alert ("Nicolas has shoe size " + student.shoeSize); }).catch(function(error) { // // Finally don't forget to catch any error // that could have happened anywhere in the // code blocks above. // alert ("Ooops: " + error); }); </script> </head> </html>
IndexedDB 目前已在美团的部分业务中使用,其余公司尚不清楚,但我相信数据量较大且须要缓存的业务必定能够用到 IndexedDB。
IndexedDB 是否能够结合 Vuex 使用?(localStorage 或许也能知足需求)
Vuex 将咱们须要共享的数据放入一个公共的变量中,以便在路由跳转时重复使用,由于路由跳转是无刷新页面的,因此数据不会丢失。可是当咱们刷新或跳转页面时,数据就会丢失。这时候就可使用 IndexedDB 或 localStorage 将数据保存在其中既可以避免数据丢失,又能避免路由跳转时没必要要的存储操做。
created () { // 页面加载时,读取 IndexedDB var data = indexedDB.getData('myDB', 'myStore', 'myData') // var data = window.localStorage.getItem('myData') var storeData = Object.assign(this.$store.state, data) this.$store.replaceState(storeData) // 页面刷新时将 vuex 里的信息保存到 IndexedDB window.addEventListener('beforeunload', () => { indexedDB.updateData('myDB', this.$store.state) // window.localStorage.setItem(this.$store.state) }) }