在知乎和我在日常工做中,經常會看到一個問題:前端
前端如今還火嗎?
這個我只想說:git
隔岸觀火的人永遠沒法明白起火的緣由,只有置身風暴,才能找到風眼之所在 ——『秦時明月』
你 TM 看都不看前端如今的發展,怎麼去評判前端火不火,我該不應嘗試一下其餘方面的內容呢?本人爲啥爲這麼熱衷於新的技術呢?主要緣由在於,生怕會被某一項顛覆性的內容淘汰掉,從前沿領域掉隊下來。說句人話就是:窮,因此只能學了...
。因此本文會從頭剖析一下 IndexedDB
在前端裏面的應用的發展。github
indexedDB 目前在前端慢慢獲得普及和應用。它正朝着前端離線數據庫技術的步伐前進。之前一開始是 manifest、localStorage、cookie 再到 webSQL,如今 indexedDB 逐漸被各大瀏覽器承認。咱們也能夠針對它來進行技術上創新的開發。好比,如今小視頻很是流行,那麼咱們能夠在用戶觀看時,經過 cacheStorage 緩存,而後利用 WebRTC 技術實現 P2P 分發的控制,不過須要注意,必定要合理利用大小,否則後果然的很嚴重。web
indexedDB 的總體架構,是由一系列單獨的概念串聯而成,所有概念以下列表。一眼看去會發現沒有任何邏輯,不過,這裏我順手畫了一幅邏輯圖,中間會根據 函數 的調用而相互串聯起來。chrome
總體邏輯圖以下:數據庫
下文主要介紹了 indexedDB 的基本概念,以及在實際應用中的實操代碼。api
index
、cursor
等高效的索引機制,推薦不要直接將全部數據都取回來,再進行篩選,而是直接利用 cursor
進行。IndexedDB 能夠存儲很是多的數據,好比 Object,files,blobs 等,裏面的存儲結構是根據 Database 來進行存儲的。每一個 DB 裏面能夠有不一樣的 object stores。具體結構以下圖:數組
而且,咱們能夠給 key
設定相關特定的值,而後在索引的時候,能夠直接經過 key 獲得具體的內容。使用 IndexDB 須要注意,其遵循的是同域原則。promise
在 indexDB 中,有幾個基本的操做對象:瀏覽器
open
方法直接打開,能夠獲得一個實例的 DB。每一個頁面能夠建立多個 DB,不過通常都是一個。idb.open(name, version, upgradeCallback)
以下 code 爲:
// 建立 index var myIndex = objectStore.index('lName');
var trans1 = db.transaction("foo", "readwrite"); var trans2 = db.transaction("foo", "readwrite"); var objectStore2 = trans2.objectStore("foo") var objectStore1 = trans1.objectStore("foo") objectStore2.put("2", "key"); objectStore1.put("1", "key");
openCursor
來進行控制。function displayData() { var transaction = db.transaction(['rushAlbumList'], "readonly"); var objectStore = transaction.objectStore('rushAlbumList'); objectStore.openCursor().onsuccess = function(event) { var cursor = event.target.result; if(cursor) { var listItem = document.createElement('li'); listItem.innerHTML = cursor.value.albumTitle + ', ' + cursor.value.year; list.appendChild(listItem); cursor.continue(); } else { console.log('Entries all displayed.'); } }; }
上面說了幾個基本的概念。那接下來咱們實踐一下 IndexDB。實際上入門 IndexDB 就是作幾個基本的內容
前期搭建一個 IndexedDB 很簡單的代碼以下:
var request = indexedDB.open(dbName, 2); request.onerror = function(event) { // 錯誤處理程序在這裏。 }; request.onupgradeneeded = function(event) { var db = event.target.result; // 設置 id 爲 primaryKey 參數 var objectStore = db.createObjectStore("customers", { keyPath: "id",{autoIncrement:true} }); // 設置指定索引,並確保惟一性 objectStore.createIndex("name", "name", { unique: false }); objectStore.createIndex("email", "email", { unique: true }); };
上面主要作了 3 件事:
打開數據庫表主要就是版本號和名字,沒有太多講的,咱們直接從建立 store 開始吧。
使用的方法就是 IDBDatabase 上的 createObjectStore
方法。
var objectStore = db.createObjectStore("customers", { keyPath: "id",{autoIncrement:true} });
基本函數構造爲:
IDBObjectStore createObjectStore(DOMString name, optional IDBObjectStoreParameters options) dictionary IDBObjectStoreParameters { (DOMString or sequence<DOMString>)? keyPath = null; boolean autoIncrement = false; };
建立的 key 主要是爲了保證,在數據插入時惟一性的標識。
不過,每每一個主鍵(key),是沒辦法很好的完成索引,在具體實踐時,就還須要輔鍵 (aid-key) 來完成輔助索引工做,這個在 IndexDB 就映射爲 index
。
在完成 PK(Primary key) 建立完畢後,爲了更好的搜索性能咱們還須要額外建立 index
。這裏能夠直接使用:
objectStore.createIndex('indexName', 'property', options);
其中,options 有三個選項:
# 建立一個名字叫 titleIndex 的 index,而且存儲的 index 不能重複 DB.createIndex('titleIndex', 'title', {unique: false});
具體能夠參考:MDN createIndex Prop 和 googleDeveloper Index。
在 IndexedDB 裏面進行數據的增刪,都須要在 transaction
中完成。而這個增刪數據,你們能夠理解爲一次 request
,至關於在一個 transaction
裏面管理全部當前邏輯操做的 request
。因此,在正式開始進行數據操做以前,還須要給你們簡單介紹一些若是建立一個事務。
transaction
API,以下 [代碼1]。在建立時,你須要手動指定當前 transaction 是那種類型的操做,基本的內容有:
upgradeneeded
回調事件裏面自動建立。它能夠用來修改現有 object store 的結構數據,好比 index 等。你能夠經過在數據庫打開以後,經過 IDBDataBase
上的 transaction
方法建立,如 [代碼2]。
[代碼1] [NewObject] IDBTransaction transaction((DOMString or sequence<DOMString>) storeNames, optional IDBTransactionMode mode = "readonly"); [代碼2] var transaction = db.transaction(["customers"], "readwrite"); var objectStore = transaction.objectStore("customers"); # 遍歷存儲數據 for (var i in customerData) { var request = objectStore.add(customerData[i]); request.onsuccess = function(event) { // success, done? }; }
事務在建立的時候不只僅能夠制定執行的模式,還能夠指定本次事務可以影響的 ObjectStore 範圍,具體細節就是在第一個 transaction
參數裏面傳入的是一個數據,而後經過 objectStore()
方法打開多個 OS 進行操做,以下 [代碼3]。
[代碼3] var tx = db.transaction(["books","person"], "readonly"); var books = tx.objectStore("books"); var person = tx.objectStore("person");
完成了事務的建立以後,咱們就能夠正式的開始進行數據的交互操做了,也就是寫咱們具體的業務邏輯。以下 [代碼1],一個完整數據事務的操做。
[代碼1] var tx = db.transaction("books", "readwrite"); var store = tx.objectStore("books"); store.put({title: "Quarry Memories", author: "Fred", isbn: 123456}); store.put({title: "Water Buffaloes", author: "Fred", isbn: 234567}); store.put({title: "Bedrock Nights", author: "Barney", isbn: 345678}); tx.oncomplete = function() { // All requests have succeeded and the transaction has committed. };
經過 objectStore
回調獲得的 IDBObjectStore 對象,咱們就能夠進行一些列的增刪查改操做了。能夠參考 [代碼2]。詳細的能夠參考文末的 appendix
。
[代碼2] [NewObject] IDBRequest put(any value, optional any key); [NewObject] IDBRequest add(any value, optional any key); [NewObject] IDBRequest delete(any query);
索引數據是全部數據庫裏面最重要的一個。這裏,咱們可使用遊標,index 來作。例如,經過 index 來快速索引 key 值,參考 [代碼1]。
[代碼1] var index = objectStore.index("name"); index.get("Donna").onsuccess = function(event) { alert("Donna's SSN is " + event.target.result.ssn); };
更詳細的內容,能夠參考下文 數據索引方式。
何謂 keyPath 和 keyGenerator 應該算是 IndexedDB 裏面比較難以理解的概念。簡單來講,IndexedDB 在建立 Store 的時候,必須保證裏面的數據是惟一的,那麼得須要像其它數據庫同樣設置一個 primary Key
來區分不一樣數據。而 keyPath 和 Generator 就是兩種不一樣的設置 key 的方式。
設置 keyPath
# 設置預先須要存放的數據 const customerData = [ { ssn: "444-44-4444", name: "Bill", age: 35, email: "bill@company.com" }, { ssn: "555-55-5555", name: "Donna", age: 32, email: "donna@home.org" } ]; # 經過 keyPath 設置 Primary Key var objectStore = db.createObjectStore("customers", { keyPath: "ssn" });
由於 ssn 在該數據集是惟一的,因此,咱們能夠利用它來做爲 keyPath
保證 unique
的特性。或者,能夠設置爲自增的鍵值,好比 id++
相似的。
upgradeDb.createObjectStore('logs', {keyPath: 'id', autoIncrement:true});
使用 generator
generator 會每次在添加數據時,自動建立一個 unique value。這個 unique value 是和你的實際數據是分開的。裏面直接經過 autoIncrement:true
來設置便可。
upgradeDb.createObjectStore('notes', {autoIncrement:true});
檢查是否支持 indexDB
if (!('indexedDB' in window)) { console.log('This browser doesn\'t support IndexedDB'); return; }
版本更新: indexDB
在生成一個 indexDB 實例時,須要手動指定一個版本號。而最經常使用的
idb.open('test-db7', 2, function(upgradeDb) {})
這樣會形成一個問題,好比上線過程當中,用戶A第一次請求返回了新版本的網頁,鏈接了版本2。以後又刷新網頁命中了另外一臺未上線的機器,鏈接了舊版本1 出錯。主要緣由是:
indexedDB API 中不容許數據庫中的數據倉庫在同一版本中發生變化. 而且當前 DB 版本不能和低版本的 version 鏈接。
好比,你一開始定義的 DB 版本內容爲:
# 版本必定義的內容 db.version(1).stores({friends: "++id,name"}); # 版本二修改結構爲: db.version(2).stores({friends: "++id,name,shoeSize"});
若是此時,用戶先打開了 version(1),可是後面,又獲得的是 version(2) 版本的 HTML,這時就會出現 error 的錯誤。
參考:
這個在 IndexDB 是一個很重要的問題。主要緣由在於
indexedDB API 中不容許數據庫中的數據倉庫在同一版本中發生變化. 而且當前 DB 版本不能和低版本的 version 鏈接。
上面就能夠抽象爲一個問題:
你什麼狀況下須要更新 IndexDB 的版本呢?
keyPath
時。# 版本 1 的 DB 設計,有一個主鍵 id 和 index-name db .version(1) .stores({friends: '++id,name'}) # 若是直接想新增一個 key,例如 male,是沒法成功的 db .version(1) .stores({friends: '++id,name,male'}) # 正確辦法是直接修改版本號更新 db .version(2) .stores({friends: '++id,name,male'})
不過,若是直接修改版本號,會出現這樣一個 case:
解決辦法就是,設置過濾,在 open
的時候,手動傳入版本號:
# 打開版本 1 的數據庫 var dbPromise = idb.open('db1', 1, function(upgradeDb){...}) # 打開版本 2 的數據庫 var dbPromise = idb.open('db2', 2, function(upgradeDb){...})
不過,這樣又會形成另一個問題,即,數據遷移(老版本數據,不可能不要吧)。這裏,IndexDB 會有一個 updateCallback 給你觸發,你能夠直接在裏面作相關的數據遷移處理。
var dbPromise = idb.open('test-db7', 2, function(upgradeDb) { switch (upgradeDb.oldVersion) { case 0: upgradeDb.createObjectStore('store', {keyPath: 'name'}); case 1: var peopleStore = upgradeDb.transaction.objectStore('store'); peopleStore.createIndex('price', 'price'); } });
在使用的時候,必定要注意 DB 版本的升級處理,好比有這樣一個 case,你的版本已是 3,不過,你須要處理版本二的數據:
# 將版本二 中的 name 拆分爲 firstName 和 lastName db.version(3).stores({friends: "++id,shoeSize,firstName,lastName"}).upgrade(function(t) { return t.friends.toCollection().modify(function(friend) { // Modify each friend: friend.firstName = friend.name.split(' ')[0]; friend.lastName = friend.name.split(' ')[1]; delete friend.name; }); });
對於存在版本 2 數據庫的用戶來講是 OK 的,可是對於某些尚未訪問過你數據庫的用戶來講,這無疑就報錯了。解決辦法有:
在 Dexie.js DB 數據庫中,須要你保留每次 DB 建立的方法,其實是經過 添加 swtich case ,來完成每一個版本的更新:
# Dexie.js 保留 DB 數據庫 db.version(1).stores({friends: "++id,name"}); db.version(2).stores({friends: "++id,name,shoeSize"}); db.version(3).stores({friends: "++id,shoeSize,firstName,lastName"}).upgrade(...) # 內部原理,直接添加 switch case 完成版本更新 var dbPromise = idb.open('test-db7', 2, function(upgradeDb) { switch (upgradeDb.oldVersion) { case 0: upgradeDb.createObjectStore('store', {keyPath: 'name'}); case 1: var peopleStore = upgradeDb.transaction.objectStore('store'); peopleStore.createIndex('price', 'price'); } });
若是遇到一個頁面打開,可是另一個頁面拉取到新的代碼進行更新時,這個時候還須要將低版本 indexedDB 進行顯式的關閉。具體操做辦法就是監聽 onversionchange
事件,當版本升級時,通知當前 DB 進行關閉,而後在新的頁面進行更新操做。
openReq.onupgradeneeded = function(event) { // 全部其它數據庫都已經被關掉了,直接更新代碼 db.createObjectStore(/* ... */); db.onversionchange = function(event) { db.close(); }; }
最後,更新是還有幾個注意事項:
有時候,咱們存儲時,想獲得一個由一串 String 生成的 hash key,那在 Web 上應該如何實現呢?
這裏能夠直接利用 Web 上已經實現的 WebCrypto,爲了實現上述需求,咱們能夠直接利用裏面的 digest
方法便可。這裏 MDN 上,已經有現成的辦法,咱們直接使用便可。
參考:
基本限制爲:
瀏覽器 | 限制 |
---|---|
Chrome | 可用空間 < 6% |
Firebox | 可用空間 < 10% |
Safari | < 50MB |
IE10 | < 250MB |
逐出策略爲:
瀏覽器 | 逐出政策 |
---|---|
Chrome | 在 Chrome 耗盡空間後採用 LRU 策略 |
Firebox | 在整個磁盤已裝滿時採用 LRU 策略 |
Safari | 無逐出 |
Edge | 無逐出 |
參考:
在數據庫中除了基本的 CRUD 外,一個高效的索引架構,則是裏面的重中之重。在 indexedDB 中,咱們一共能夠經過三種方式來索引數據:
IDBObjectStore 提供給了咱們直接經過 primaryKey
來索引數據,參考 [代碼1],這種方式須要咱們一開始就知道目標的 key
內容。固然,也能夠經過 getAll
所有索引數據。
[代碼1] [NewObject] IDBRequest get(any query); [NewObject] IDBRequest getKey(any query); [NewObject] IDBRequest getAll(optional any query, optional [EnforceRange] unsigned long count); [NewObject] IDBRequest getAllKeys(optional any query, optional [EnforceRange] unsigned long count);
好比,咱們經過 primaryKey 獲得一條具體的數據:
db.transaction("customers").objectStore("customers").get("id_card_1118899").onsuccess = function(event) { // data is event.target.result.name };
也能夠 fetch 整個 Object Store 的數據。這些場景用處比較少,這裏就不過多講解。咱們主要來了解一下 index 的索引方式。
若是想要查詢某個數據,直接經過整個對象來進行遍歷的話,這樣作性能耗時是很是大的。若是咱們結合 index
來將 key 加以分類,就能夠很快速的實現指定數據的索引。這裏,咱們能夠直接利用 IDBObjectStore 上面的 index()
方法來獲取指定 index 的值,具體方法能夠參考 [代碼1]。
[代碼1] IDBIndex index(DOMString name);
該方法會直接返回一個 IDBIndex 對象。這你也能夠理解爲一個相似 ObjectStore 的微型 index 數據內容。接着,咱們可使用 get()
方法來得到指定 index 的數據,參考[代碼2]。
[代碼2] var index = objectStore.index("name"); index.get("Donna").onsuccess = function(event) { alert("Donna's SSN is " + event.target.result.ssn); };
使用 get
方法無論你的 index 是不是 unique
的都會只會返回第一個數據。若是想獲得多個數據的話,可使用 getAll(key)
來作。經過 getAll()
獲得的回調函數,直接經過 event.target.result
能夠獲得對應的 value 內容。
objectStore.getAll().onsuccess = function(event) { printf(event.target.result); // Array };
除了經過 getAll()
獲得全部數據外,還能夠採用更高效的 cursor
方法遍歷獲得的數據。
參考:
所謂的遊標,你們內心應該能夠有一個初步的印象,就像咱們物理尺子上的那個東西,能夠自由的移動,來標識指向的對象內容。cursor 裏面有兩個核心的方法:
好比,咱們使用 cursor 來遍歷 Object Store 的具體數據。
objectStore.openCursor().onsuccess = function(event) { var cursor = event.target.result; if(cursor) { // cursor.key // cursor.value cursor.continue(); } else { console.log('Entries all displayed.'); } };
一般,遊標能夠用來遍歷兩個類型的數據,一個是 ObjectStore、一個是 Index。
primaryKey
遍歷整個數據,注意,這裏不會存在重複的狀況,由於 primaryKey
是惟一的。在 IDBObjectStore 對象上有兩種方法來打開遊標:
這裏,咱們經過 openCursor
來直接打開一個 index 數據集,而後進行遍歷。
PersonIndex.openCursor().onsuccess = function(event) { var cursor = event.target.result; if (cursor) { customers.push(cursor.value); cursor.continue(); } else { alert("Got all customers: " + customers); } };
在遊標中,還提供給了一個 update
和 delete
方法,咱們能夠用它來進行數據的更新操做,不然的話就直接使用 ObjectStore 提供的 put
方法。
遊標裏面咱們還能夠限定其遍歷的範圍和方向。這個設置是咱們直接在 openCursor()
方法裏面傳參完成的,該方法的構造函數參考 [代碼1]。他裏面能夠傳入兩個參數,第一個用來指定範圍,第二個用來指定 cursor
移動的方向。
[代碼1] IDBRequest openCursor(optional any query, optional IDBCursorDirection direction = "next");
若是須要對 cursor 設置範圍的話,就須要使用到 IDBKeyRange
這個對象,使用樣板能夠參考 [代碼2]。IDBKeyRange 裏面 key 參考的對象 因使用者的不一樣而不一樣。若是是針對 ObjectStore 的話,則是針對 primaryKey,若是是針對 Index 的話,則是針對當前的 indexKey
/ 匹配全部在 「Bill」 前面的, 可是不須要包括 "Bill" var lowerBoundOpenKeyRange = IDBKeyRange.lowerBound("Bill", true);
好比,咱們這裏對 PersonIndex 設置一個 index 範圍,即,索引 在 villainhr
和 jimmyVV
之間的數據集合。
# 都包括 villainhr 和 jimmyVV 的數據 var boundKeyRange = IDBKeyRange.bound("villainhr", "jimmyVV", true, true); PersonIndex.openCursor(boundKeyRange).onsuccess = function(event) { var cursor = event.target.result; if (cursor) { // Do something with the matches. cursor.continue(); } };
若是你還想設置遍歷的方向和是否排除重複數據,還能夠根據 [代碼2] 的枚舉類型來設置。好比,在 [代碼3] 中,咱們改變默認的 cursor 遍歷數據的方向爲 prev
,從末尾開始。
[代碼2] enum IDBCursorDirection { "next", "nextunique", "prev", "prevunique" }; [代碼3] objectStore.openCursor(null, IDBCursor.prev).onsuccess = function(event) { var cursor = event.target.result; if (cursor) { // cursor.value cursor.continue(); } };
在 indexDB 裏面的讀寫所有是基於 transaction
模式來的。也就是 IDBDataBase 裏面的 transaction
方法,以下 [代碼1]。全部的讀寫均可以比做在 transaction
做用域下的請求,只有當全部請求完成以後,該次 transaction
纔會生效,不然就會拋出異常或者錯誤。transaction
會根據監聽 error,abort,以及 complete 三個事件來完成整個事務的流程管理,參考[代碼2]。
[代碼1] [NewObject] IDBTransaction transaction((DOMString or sequence<DOMString>) storeNames, optional IDBTransactionMode mode = "readonly"); [代碼2] attribute EventHandler onabort; attribute EventHandler oncomplete; attribute EventHandler onerror;
例如:
var request = db.transaction(["customers"], "readwrite") .objectStore("customers") .delete("gg"); request.onsuccess = function(event) { // delete, done };
你能夠在 transaction
方法裏面手動傳入 readwrite
或者其餘表示事務的 readonly
參數,來表示本次事務你會進行如何的操做。IndexedDB 在初始設計時,就已經決定了它的性能問題。
只含有 readonly 模式的 transaction 能夠併發進行執行
含有 write 模式的 transaction 必須按照隊列 來 執行
這就意味着,若是你使用了 readwrite
模式的話,那麼後續無論是否是 readonly
都必須等待該次 transaction 完成才行。
指定 primaryKey 生成時,是經過 createObjectStore
方法來操做的。有時候,咱們會遇到想直接獲得一個 key,而且存在於當前數據集中,能夠在 options 中同時加上 keyPath
和 autoIncrement
屬性。該 key 的範圍是 [1- $ 2^{53} $],參考 keygenerator key 的大小
db.createObjectStore('table1', {keyPath: 'id', autoIncrement: true});
閱讀推薦
indexedDB W3C 文檔
indexedDB 入門
MDN indexedDB 入門
好用庫推薦
在如下狀況下,數據庫可能被清除:
數據庫
數據庫: 一般包含一個或多個 object stores. 每一個數據庫必須包含如下內容:
對象存儲(object store): 用來承載數據的一個分區.數據以鍵值對形式被對象存儲永久持有。在 OS 中,建立一個 key 可使用 key generator
和 key path
。
具體數據 key/value
key: 這個 key 的值,能夠經過三種方式生成。 a key generator, a key path, 用戶指定的值。而且,這個 key 在當前的 Object Store 是惟一的。一個 key 類型能夠是 string, date, float, and array 類型。不過,在老版本的時候,通常只支持 string or integer。(如今,版本應該都 OK 了)
- key generator: 至關於以一種 `id++` 的形式來生成一個 key 值。 - key path: 當前指定的 key 能夠根據 value 裏面的內容來指定。裏面能夠爲一些分隔符。 - 指定的 key:這個就是須要用戶手動來指定生成。
操做做用域
key range: 用來設置取出數據的 key 的範圍內容。
參考:
這其實就是 indexDB
上面掛載的對象。主要 API 以下:
[Exposed=(Window,Worker)] interface IDBFactory { [NewObject] IDBOpenDBRequest open(DOMString name, optional [EnforceRange] unsigned long long version); [NewObject] IDBOpenDBRequest deleteDatabase(DOMString name); short cmp(any first, any second); };
你能夠直接經過 open
來打開一個數據庫。經過 返回一個 Request 對象,來進行結果監聽的回調:
var request = indexedDB.open('AddressBook', 15); request.onsuccess = function(evt) {...}; request.onerror = function(evt) {...};
參考:
當你經過 open
方法處理事後,就會獲得一個 Request 回調對象。這個就是 IDBRequest 的實例。
[Exposed=(Window,Worker)] interface IDBRequest : EventTarget { readonly attribute any result; // 經過 open 打開事後的 IDBObjectStore 實例 readonly attribute DOMException? error; readonly attribute (IDBObjectStore or IDBIndex or IDBCursor)? source; readonly attribute IDBTransaction? transaction; readonly attribute IDBRequestReadyState readyState; // Event handlers: attribute EventHandler onsuccess; attribute EventHandler onerror; }; enum IDBRequestReadyState { "pending", "done" }; [Exposed=(Window,Worker)] interface IDBOpenDBRequest : IDBRequest { // Event handlers: attribute EventHandler onblocked; attribute EventHandler onupgradeneeded; };
你能夠經過 result
獲得當前數據庫操做的結果。若是你打開更新後的版本號的話,還須要監聽 onupgradeneeded
事件來實現。最常經過 indexedDB.open 碰見的錯誤就是 VER_ERR
版本錯誤。這代表存儲在磁盤上的數據庫的版本高於你試圖打開的版本。
db.onerror = function(event) { // Generic error handler for all errors targeted at this database's // requests! alert("Database error: " + event.target.errorCode); };
因此,通常在建立 IndexDB 時,還須要管理它版本的更新操做,這裏就須要監聽 onupgradeneeded 來是實現。
request.onupgradeneeded = function(event) { // 更新對象存儲空間和索引 .... };
或者咱們能夠直接使用 idb
微型庫來實現讀取操做。
var dbPromise = idb.open('test-db3', 1, function(upgradeDb) { if (!upgradeDb.objectStoreNames.contains('people')) { upgradeDb.createObjectStore('people', {keyPath: 'email'}); } if (!upgradeDb.objectStoreNames.contains('notes')) { upgradeDb.createObjectStore('notes', {autoIncrement: true}); } if (!upgradeDb.objectStoreNames.contains('logs')) { upgradeDb.createObjectStore('logs', {keyPath: 'id', autoIncrement: true}); } });
其中經過 onupgradeneeded
回調獲得的 event.result 就是 IDBDatabase
的實例,經常用來設置 index 和插入數據。參考下面內容。
參考:
該對象經常用來作 Object Store 和 transaction 的建立和刪除。該部分是 onupgradeneeded
事件得到的 event.target.result
對象:
request.onupgradeneeded = function(event) { // 更新對象存儲空間和索引 .... // event.target.result 對象 };
具體 API 內容以下:
[Exposed=(Window,Worker)] interface IDBDatabase : EventTarget { readonly attribute DOMString name; readonly attribute unsigned long long version; readonly attribute DOMStringList objectStoreNames; [NewObject] IDBTransaction transaction((DOMString or sequence<DOMString>) storeNames, optional IDBTransactionMode mode = "readonly"); void close(); [NewObject] IDBObjectStore createObjectStore(DOMString name, optional IDBObjectStoreParameters options); void deleteObjectStore(DOMString name); // Event handlers: attribute EventHandler onabort; attribute EventHandler onclose; attribute EventHandler onerror; attribute EventHandler onversionchange; }; dictionary IDBObjectStoreParameters { (DOMString or sequence<DOMString>)? keyPath = null; boolean autoIncrement = false; };
若是它經過 createObjectStore 方法,那麼獲得的就是一個 IDBObjectStore
實例對象。若是是 transaction 方法,那麼就是 IDBTransaction
對象。
該對象通常是用來建立 index 和插入數據使用。
能夠參考:
[Exposed=(Window,Worker)] interface IDBObjectStore { attribute DOMString name; readonly attribute any keyPath; readonly attribute DOMStringList indexNames; [SameObject] readonly attribute IDBTransaction transaction; readonly attribute boolean autoIncrement; [NewObject] IDBRequest put(any value, optional any key); [NewObject] IDBRequest add(any value, optional any key); [NewObject] IDBRequest delete(any query); [NewObject] IDBRequest clear(); [NewObject] IDBRequest get(any query); [NewObject] IDBRequest getKey(any query); [NewObject] IDBRequest getAll(optional any query, optional [EnforceRange] unsigned long count); [NewObject] IDBRequest getAllKeys(optional any query, optional [EnforceRange] unsigned long count); [NewObject] IDBRequest count(optional any query); [NewObject] IDBRequest openCursor(optional any query, optional IDBCursorDirection direction = "next"); [NewObject] IDBRequest openKeyCursor(optional any query, optional IDBCursorDirection direction = "next"); IDBIndex index(DOMString name); [NewObject] IDBIndex createIndex(DOMString name, (DOMString or sequence<DOMString>) keyPath, optional IDBIndexParameters options); void deleteIndex(DOMString name); }; dictionary IDBIndexParameters { boolean unique = false; boolean multiEntry = false; };
該對象是用來進行 Index 索引的操做對象,裏面也會存在 get
和 getAll
等方法。詳細內容以下:
[Exposed=(Window,Worker)] interface IDBIndex { attribute DOMString name; [SameObject] readonly attribute IDBObjectStore objectStore; readonly attribute any keyPath; readonly attribute boolean multiEntry; readonly attribute boolean unique; [NewObject] IDBRequest get(any query); [NewObject] IDBRequest getKey(any query); [NewObject] IDBRequest getAll(optional any query, optional [EnforceRange] unsigned long count); [NewObject] IDBRequest getAllKeys(optional any query, optional [EnforceRange] unsigned long count); [NewObject] IDBRequest count(optional any query); [NewObject] IDBRequest openCursor(optional any query, optional IDBCursorDirection direction = "next"); [NewObject] IDBRequest openKeyCursor(optional any query, optional IDBCursorDirection direction = "next"); };
參考:
idb 開源庫,微型代碼庫
treo 開源庫
dexie.js 開源庫
indexeddb
原生概念 IndexedDB
也歡迎你們關注個人公衆號:前端小吉米 得到一手的技術文章以及將來技術的發展內容。