本文經過對IndexedDB
的使用方法和使用場景進行相關介紹,對常見的問題進行解答。javascript
同時,由於MDN中的相關文檔缺少相關邏輯性,因此不容易理解。本文將經過項目中常見的數據存儲和操做需求來進行內容組織。java
讀者可以經過本文學會在項目中正確的使用IndexedDB
,給應用帶來的本地存儲能力,而且避免一些常見的問題。git
當咱們進行一些較大的SPA頁面開發時,咱們會須要進行一些數據的本地存儲。github
當數據量不大時,咱們能夠經過SessionStorage或者LocalStorage來進行存儲,可是當數據量較大,或符合必定的規範時,咱們可使用數據庫來進行數據的存儲。web
在瀏覽器提供的數據庫中,共有web sql
和IndexedDB
兩種。相較於HTML5已經廢棄的web sql
來講,更推薦你們使用IndexedDB
。sql
下面,咱們經過一張圖來了解下,IndexedDB總體的結構。數據庫
類比sql
型數據庫,IndexedDB
中的DB(數據庫)就是sql
中的DB,而Object Store(存儲空間)
則是數據表
,Item
則等於表中的一條記錄
。segmentfault
如今,咱們將其根據IndexedDB
的結構來對其操做進行介紹,能讓你們對這個存儲空間有一個初步的瞭解。咱們主要介紹:瀏覽器
使用IndexedDB
第一步,就是建立或打開一個數據庫。咱們使用window.indexedDB.open(DBName)
這個API來打進行操做。具體示例以下:緩存
const request = window.indexedDB.open('test'); request.onupgradeneeded = function (event) { } request.onsuccess = function(event) { //request === event.target; } request.onerror = function(event) {}
調用此接口時,若是當前數據庫不存在,則會建立一個新的數據庫。
當數據庫創建鏈接時,會返回一個IDBOpenDBRequest
對象。
在鏈接創建成功時,會觸發onsuccess
事件,其中函數參數event
的target
屬性就是request
對象。
而在數據庫建立或者版本更新時,會觸發onupgradeneeded
事件。
window.indexedDB.open
的第二個參數即爲版本號。在不指定的狀況下,默認版本號爲1。具體示例以下:
const request = window.indexedDB.open('test', 2);
在須要更新數據庫的schema(模式)
時,須要更新版本號。此時咱們指定一個高於以前版本的版本號,就會觸發onupgradeneeded
事件。相似的,當此數據庫不存在時,也會觸發此事件而且將版本更新到置頂版本。
咱們須要注意的是,版本號是一個Unsigned long long
數字,這意味着它能夠是一個很是大的整數。可是,它不能是一個小數,不然它將會被轉爲最近的整數,同時有可能致使onUpgradeneeded
事件不觸發(bug)。
咱們使用createObjectStore
來建立一個存儲空間。同時,使用createIndex
來建立它的索引。具體示例以下:
var request = window.indexedDB.open('test', 1); request.onupgradeneeded = function (event) { var db = event.target.result; var objectStore = db.createObjectStore('table1', {keyPath: 'id', autoIncrement: true}); objectStore.createIndex('name', 'name', {unique: false}); } request.onerror = function (event) { alert("Why didn't you allow my web app to use IndexedDB?!"); };
注:只能在onupgradeneeded
回調函數中建立存儲空間,而不能在數據庫打開後的success
回調函數中建立。
經過createObjectStore
可以建立一個存儲空間。接受兩個參數:
customers
。keyPath
值爲存儲對象的某個屬性,這個屬性可以在獲取存儲空間數據的時候當作key值使用。autoIncrement
指定了key
值是否自增(當key值爲默認的從1開始到2^53的整數時)。而createIndex
可以給當前的存儲空間設置一個索引。它接受三個參數:
unique
的值爲true
表示不容許索引值相等。在IndexedDB
中,咱們也可以使用事務來進行數據庫的操做。事務有三個模式(常量已經棄用):
readOnly
,只讀。readwrite
,讀寫。versionchange
,數據庫版本變化。咱們建立一個事務時,須要從上面選擇一種模式,若是不指定的話,則默認爲只讀模式。具體示例以下:
const transaction = db.transaction(['customers'], 'readwrite');
事務函數transaction
的第一個參數爲須要關聯的存儲空間,第二個可選參數爲事務模式。與上面相似,事務成功時也會觸發onsuccess
函數,失敗時觸發onerror
函數。
事務的操做都是原子性的。
當存儲空間初始化完成後,咱們能夠把數據放入存儲空間中。直接調用add
方法就能夠將數據放入存儲空間內,具體示例以下:
var request = window.indexedDB.open('test', 1); request.onsuccess = function (event) { var db = event.target.result; var transaction = db.transaction(['table1'], 'readwrite'); var objectStore = transaction.objectStore('table1'); var index = objectStore.index('name'); objectStore.add({name: 'a', age: 10}); objectStore.add({name: 'b', age: 20}); }
注:add
方法中的第二個參數key值是指定存儲空間中的keyPath
值,若是data
中包含keyPath
值或者此值爲自增值,那麼能夠略去此參數。
當咱們須要從存儲空間獲取數據時,咱們能夠經過如下的方法:
var request = window.indexedDB.open('test', 1); request.onsuccess = function (event) { var db = event.target.result; var transaction = db.transaction(['table1'], 'readwrite'); var objectStore = transaction.objectStore('table1'); var request = objectStore.get(1); request.onsuccess = function (event) { // 對 request.result 作些操做! console.log(request.result); }; request.onerror = function (event) { // 錯誤處理! }; }
當你須要便利整個存儲空間中的數據時,你就須要使用到遊標。遊標使用方法以下:
var request = window.indexedDB.open('test', 1); request.onsuccess = function (event) { var db = event.target.result; var transaction = db.transaction(['table1'], 'readwrite'); var objectStore = transaction.objectStore('table1'); var request = objectStore.openCursor(); request.onsuccess = function (event) { var cursor = event.target.result; if (cursor) { // 使用Object.assign方法是爲了不控制檯打印時出錯 console.log(Object.assign(cursor.value)); cursor.continue(); } }; request.onerror = function (event) { // 錯誤處理! }; }
使用遊標時有一個須要注意的地方,當遊標便利整個存儲空間可是並未找到給定條件的值時,仍然會觸發onsuccess
函數。
openCursor
和openKeyCursor
有兩個參數:
第一個參數,遍歷範圍,指定遊標的訪問範圍。該範圍經過一個IDBKeyRange
參數的方法來獲取。
遍歷範圍參數具體示例以下:
// 匹配值 key === 1 const singleKeyRange = IDBKeyRange.only(1); // 匹配值 key >= 1 const lowerBoundKeyRange = IDBKeyRange.lowerBound(1); // 匹配值 key > 1 const lowerBoundOpenKeyRange = IDBKeyRange.lowerBound(1, true); // 匹配值 key < 2 const upperBoundOpenKeyRange = IDBKeyRange.upperBound(2, true); // 匹配值 key >= 1 && key < 2 const boundKeyRange = IDBKeyRange.bound(1, 2, false, true); index.openCursor(boundKeyRange).onsuccess = function(event) { const cursor = event.target.result; if (cursor) { // Do something with the matches. cursor.continue(); } };
第二個參數,便利順序,指定遊標便利時的順序和處理相同id(keyPath屬性指定字段)重複時的處理方法。改範圍經過特定的字符串(IDBCursor
的常量已經棄用)來獲取。其中:
next
,從前日後獲取全部數據(包括重複數據)prev
,從後往前獲取全部數據(包括重複數據)nextunique
,從前日後獲取數據(重複數據只取第一條,索引重複即認爲重複,下同)prevunique
,從後往前獲取數據(重複數據只取第一條)遍歷順序參數具體示例以下:
var request = window.indexedDB.open('test', 1); request.onsuccess = function (event) { var db = event.target.result; var transaction = db.transaction(['table1'], 'readwrite'); var objectStore = transaction.objectStore('table1'); var lowerBoundOpenKeyRange = IDBKeyRange.lowerBound(1, false); var request = objectStore.openCursor(lowerBoundOpenKeyRange, IDBCursor.PREV); request.onsuccess = function (event) { var cursor = event.target.result; if (cursor) { // 使用Object.assign方法是爲了不控制檯打印時出錯 console.log(Object.assign(cursor.value)); cursor.continue(); } }; request.onerror = function (event) { // 錯誤處理! }; }
在前面構建數據庫時,咱們建立了兩個索引。如今咱們也能夠經過索引來進行數據檢索。他的本質仍是經過以前獲取數據的API來進行,只是將原來使用的keyPath
屬性轉換成爲了索引指定的屬性。具體示例以下:
var request = window.indexedDB.open('test', 1); request.onsuccess = function (event) { var db = event.target.result; var transaction = db.transaction(['table1'], 'readwrite'); var objectStore = transaction.objectStore('table1'); var index = objectStore.index('name'); // 第一種,get方法 index.get('a').onsuccess = function (event) { console.log(event.target.result); } // 第二種,普通遊標方法 index.openCursor().onsuccess = function (event) { console.log('openCursor:', event.target.result.value); } // 第三種,鍵遊標方法,該方法與第二種的差異爲:普通遊標帶有value值表示獲取的數據,而鍵遊標沒有 index.openKeyCursor().onsuccess = function (event) { console.log('openKeyCursor:', event.target.result); } }
當須要修改存儲空間中的數據時,咱們可使用如下的API:
var objectStore = transaction.objectStore("customers"); var request = objectStore.put(data); request.onsuccess = function (event) { }
注:put
方法不只可以修改現有數據,也可以往存儲空間中增長新的數據。
當咱們須要刪除已經無用的數據時,咱們能夠經過如下方法:
var objectStore = transaction.objectStore("customers"); var request = objectStore.delete(name); request.onsuccess = function (event) { }
在瀏覽器有以下操做的狀況下,indexedDB可能會出現異常:
此時,須要對錯誤進行捕獲,而且對用戶進行提示。此章節不是本文重點,再此略過。
在IndexedDB
中,鍵值對中的key
值能夠接受如下幾種類型的值:
具體說明能夠見文檔此處。
當一個key
值變爲主鍵,即keyPath
時,它的值就只能是如下幾種:
注:空格不能出如今key path中。
具體說明能夠見文檔此處。
在IndexedDB
中,value可以接受ECMA-262中全部的類型的值,例如String,Date,ImageDate等。
IndexedDB
在沒有指定key值的時候就會採用自增的key值。若是一個事務在中途中斷,那麼key值的自增將會從中斷的事務開始前的key開始。
IndexedDB
也受到瀏覽器同源策略的限制。
用戶在清除瀏覽器緩存時,可能會清除IndexedDB
中相關的數據。
部分瀏覽器如Safari手機版隱私模式在訪問IndexedDB
時,可能會出現因爲沒有權限而致使的異常(LocalStorage也會),須要進行異常處理。
IndexedDB
在本地存儲中有着無可替代的做用,是替代關係型數據庫web sql
的產品,可以對大量數據進行存儲。在許多須要運用離線存儲的場景下,它可以給咱們提供有效的支撐。
可是,IndexedDB
在使用過程當中仍然須要避免可能會出現的一些問題,或者對可能致使的不利影響有必定的容錯處理。這樣纔不會對應用產生重大影響。