提及 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) }) }