摘要:在不指定的情況下,默認(rèn)版本號為。具體示例如下在需要更新數(shù)據(jù)庫的模式時,需要更新版本號。此時我們指定一個高于之前版本的版本號,就會觸發(fā)事件。數(shù)據(jù)操作事務(wù)在中,我們也能夠使用事務(wù)來進(jìn)行數(shù)據(jù)庫的操作。
概述
本文通過對IndexedDB的使用方法和使用場景進(jìn)行相關(guān)介紹,對常見的問題進(jìn)行解答。
同時,因為MDN中的相關(guān)文檔缺乏相關(guān)邏輯性,所以不容易理解。本文將通過項目中常見的數(shù)據(jù)存儲和操作需求來進(jìn)行內(nèi)容組織。
讀者能夠通過本文學(xué)會在項目中正確的使用IndexedDB,給應(yīng)用帶來的本地存儲能力,并且避免一些常見的問題。
原因:開發(fā)者需要在本地進(jìn)行永久存儲當(dāng)我們進(jìn)行一些較大的SPA頁面開發(fā)時,我們會需要進(jìn)行一些數(shù)據(jù)的本地存儲。
當(dāng)數(shù)據(jù)量不大時,我們可以通過SessionStorage或者LocalStorage來進(jìn)行存儲,但是當(dāng)數(shù)據(jù)量較大,或符合一定的規(guī)范時,我們可以使用數(shù)據(jù)庫來進(jìn)行數(shù)據(jù)的存儲。
在瀏覽器提供的數(shù)據(jù)庫中,共有web sql和IndexedDB兩種。相較于HTML5已經(jīng)廢棄的web sql來說,更推薦大家使用IndexedDB。
結(jié)構(gòu)下面,我們通過一張圖來了解下,IndexedDB整體的結(jié)構(gòu)。
類比sql型數(shù)據(jù)庫,IndexedDB中的DB(數(shù)據(jù)庫)就是sql中的DB,而Object Store(存儲空間)則是數(shù)據(jù)表,Item則等于表中的一條記錄。
使用IndexedDB現(xiàn)在,我們將其根據(jù)IndexedDB的結(jié)構(gòu)來對其操作進(jìn)行介紹,能讓大家對這個存儲空間有一個初步的了解。我們主要介紹:
數(shù)據(jù)庫操作
數(shù)據(jù)表操作
數(shù)據(jù)操作
數(shù)據(jù)庫操作 創(chuàng)建或打開數(shù)據(jù)庫使用IndexedDB第一步,就是創(chuàng)建或打開一個數(shù)據(jù)庫。我們使用window.indexedDB.open(DBName)這個API來打進(jìn)行操作。具體示例如下:
const request = window.indexedDB.open("test"); request.onupgradeneeded = function (event) { } request.onsuccess = function(event) { //request === event.target; } request.onerror = function(event) {}
調(diào)用此接口時,如果當(dāng)前數(shù)據(jù)庫不存在,則會創(chuàng)建一個新的數(shù)據(jù)庫。
當(dāng)數(shù)據(jù)庫建立連接時,會返回一個IDBOpenDBRequest對象。
在連接建立成功時,會觸發(fā)onsuccess事件,其中函數(shù)參數(shù)event的target屬性就是request對象。
而在數(shù)據(jù)庫創(chuàng)建或者版本更新時,會觸發(fā)onupgradeneeded事件。
更新數(shù)據(jù)庫版本號window.indexedDB.open的第二個參數(shù)即為版本號。在不指定的情況下,默認(rèn)版本號為1。具體示例如下:
const request = window.indexedDB.open("test", 2);
在需要更新數(shù)據(jù)庫的schema(模式)時,需要更新版本號。此時我們指定一個高于之前版本的版本號,就會觸發(fā)onupgradeneeded事件。類似的,當(dāng)此數(shù)據(jù)庫不存在時,也會觸發(fā)此事件并且將版本更新到置頂版本。
我們需要注意的是,版本號是一個Unsigned long long數(shù)字,這意味著它可以是一個非常大的整數(shù)。但是,它不能是一個小數(shù),否則它將會被轉(zhuǎn)為最近的整數(shù),同時有可能導(dǎo)致onUpgradeneeded事件不觸發(fā)(bug)。
存儲空間操作 創(chuàng)建存儲空間我們使用createObjectStore來創(chuàng)建一個存儲空間。同時,使用createIndex來創(chuàng)建它的索引。具體示例如下:
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回調(diào)函數(shù)中創(chuàng)建存儲空間,而不能在數(shù)據(jù)庫打開后的success回調(diào)函數(shù)中創(chuàng)建。
通過createObjectStore能夠創(chuàng)建一個存儲空間。接受兩個參數(shù):
第一個參數(shù),存儲空間的名稱,即我們上面的customers。
第二個參數(shù),指定存儲的keyPath值為存儲對象的某個屬性,這個屬性能夠在獲取存儲空間數(shù)據(jù)的時候當(dāng)做key值使用。autoIncrement指定了key值是否自增(當(dāng)key值為默認(rèn)的從1開始到2^53的整數(shù)時)。
而createIndex能夠給當(dāng)前的存儲空間設(shè)置一個索引。它接受三個參數(shù):
第一個參數(shù),索引的名稱。
第二個參數(shù),指定根據(jù)存儲數(shù)據(jù)的哪一個屬性來構(gòu)建索引。
第三個屬性, options對象,其中屬性unique的值為true表示不允許索引值相等。
數(shù)據(jù)操作 事務(wù)在IndexedDB中,我們也能夠使用事務(wù)來進(jìn)行數(shù)據(jù)庫的操作。事務(wù)有三個模式(常量已經(jīng)棄用):
readOnly,只讀。
readwrite,讀寫。
versionchange,數(shù)據(jù)庫版本變化。
我們創(chuàng)建一個事務(wù)時,需要從上面選擇一種模式,如果不指定的話,則默認(rèn)為只讀模式。具體示例如下:
const transaction = db.transaction(["customers"], "readwrite");
事務(wù)函數(shù)transaction的第一個參數(shù)為需要關(guān)聯(lián)的存儲空間,第二個可選參數(shù)為事務(wù)模式。與上面類似,事務(wù)成功時也會觸發(fā)onsuccess函數(shù),失敗時觸發(fā)onerror函數(shù)。
事務(wù)的操作都是原子性的。
增加數(shù)據(jù)當(dāng)存儲空間初始化完成后,我們可以把數(shù)據(jù)放入存儲空間中。直接調(diào)用add方法就可以將數(shù)據(jù)放入存儲空間內(nèi),具體示例如下:
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方法中的第二個參數(shù)key值是指定存儲空間中的keyPath值,如果data中包含keyPath值或者此值為自增值,那么可以略去此參數(shù)。
查找數(shù)據(jù) 通過特定值獲取數(shù)據(jù)當(dāng)我們需要從存儲空間獲取數(shù)據(jù)時,我們可以通過以下的方法:
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) { // 錯誤處理! }; }通過游標(biāo)獲取數(shù)據(jù)
當(dāng)你需要便利整個存儲空間中的數(shù)據(jù)時,你就需要使用到游標(biāo)。游標(biāo)使用方法如下:
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) { // 錯誤處理! }; }
使用游標(biāo)時有一個需要注意的地方,當(dāng)游標(biāo)便利整個存儲空間但是并未找到給定條件的值時,仍然會觸發(fā)onsuccess函數(shù)。
openCursor和openKeyCursor有兩個參數(shù):
第一個參數(shù),遍歷范圍,指定游標(biāo)的訪問范圍。該范圍通過一個IDBKeyRange參數(shù)的方法來獲取。
遍歷范圍參數(shù)具體示例如下:
// 匹配值 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(); } };
?
第二個參數(shù),便利順序,指定游標(biāo)便利時的順序和處理相同id(keyPath屬性指定字段)重復(fù)時的處理方法。改范圍通過特定的字符串(IDBCursor的常量已經(jīng)棄用)來獲取。其中:
next,從前往后獲取所有數(shù)據(jù)(包括重復(fù)數(shù)據(jù))
prev,從后往前獲取所有數(shù)據(jù)(包括重復(fù)數(shù)據(jù))
nextunique,從前往后獲取數(shù)據(jù)(重復(fù)數(shù)據(jù)只取第一條,索引重復(fù)即認(rèn)為重復(fù),下同)
prevunique,從后往前獲取數(shù)據(jù)(重復(fù)數(shù)據(jù)只取第一條)
遍歷順序參數(shù)具體示例如下:
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) { // 錯誤處理! }; }使用索引
在前面構(gòu)建數(shù)據(jù)庫時,我們創(chuàng)建了兩個索引。現(xiàn)在我們也可以通過索引來進(jìn)行數(shù)據(jù)檢索。他的本質(zhì)還是通過之前獲取數(shù)據(jù)的API來進(jìn)行,只是將原來使用的keyPath屬性轉(zhuǎn)換成為了索引指定的屬性。具體示例如下:
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); } // 第二種,普通游標(biāo)方法 index.openCursor().onsuccess = function (event) { console.log("openCursor:", event.target.result.value); } // 第三種,鍵游標(biāo)方法,該方法與第二種的差別為:普通游標(biāo)帶有value值表示獲取的數(shù)據(jù),而鍵游標(biāo)沒有 index.openKeyCursor().onsuccess = function (event) { console.log("openKeyCursor:", event.target.result); } }修改數(shù)據(jù)
當(dāng)需要修改存儲空間中的數(shù)據(jù)時,我們可以使用以下的API:
var objectStore = transaction.objectStore("customers"); var request = objectStore.put(data); request.onsuccess = function (event) { }
注:put方法不僅能夠修改現(xiàn)有數(shù)據(jù),也能夠往存儲空間中增加新的數(shù)據(jù)。
刪除數(shù)據(jù)當(dāng)我們需要刪除已經(jīng)無用的數(shù)據(jù)時,我們可以通過以下方法:
var objectStore = transaction.objectStore("customers"); var request = objectStore.delete(name); request.onsuccess = function (event) { }異常處理
在瀏覽器有如下操作的情況下,indexedDB可能會出現(xiàn)異常:
用戶清除瀏覽器緩存
存儲空間超過大小限制
此時,需要對錯誤進(jìn)行捕獲,并且對用戶進(jìn)行提示。此章節(jié)不是本文重點,再此略過。
擴(kuò)展須知 取值相關(guān) key值能夠接受的數(shù)據(jù)類型在IndexedDB中,鍵值對中的key值可以接受以下幾種類型的值:
number
data
string
binary
array
具體說明可以見文檔此處。
key path能夠接受的數(shù)據(jù)類型當(dāng)一個key值變?yōu)橹麈I,即keyPath時,它的值就只能是以下幾種:
Blob
File
Array
String
注:空格不能出現(xiàn)在key path中。
具體說明可以見文檔此處。
value能夠接受的數(shù)據(jù)類型在IndexedDB中,value能夠接受ECMA-262中所有的類型的值,例如String,Date,ImageDate等。
事務(wù)相關(guān) 事務(wù)中斷后,會不會影響key值的自增IndexedDB在沒有指定key值的時候就會采用自增的key值。如果一個事務(wù)在中途中斷,那么key值的自增將會從中斷的事務(wù)開始前的key開始。
安全相關(guān)IndexedDB也受到瀏覽器同源策略的限制。
用戶相關(guān) 清空緩存用戶在清除瀏覽器緩存時,可能會清除IndexedDB中相關(guān)的數(shù)據(jù)。
訪問權(quán)限部分瀏覽器如Safari手機(jī)版隱私模式在訪問IndexedDB時,可能會出現(xiàn)由于沒有權(quán)限而導(dǎo)致的異常(LocalStorage也會),需要進(jìn)行異常處理。
總結(jié)IndexedDB在本地存儲中有著無可替代的作用,是替代關(guān)系型數(shù)據(jù)庫web sql的產(chǎn)品,能夠?qū)Υ罅繑?shù)據(jù)進(jìn)行存儲。在許多需要運用離線存儲的場景下,它能夠給我們提供有效的支撐。
但是,IndexedDB在使用過程中仍然需要避免可能會出現(xiàn)的一些問題,或者對可能導(dǎo)致的不利影響有一定的容錯處理。這樣才不會對應(yīng)用產(chǎn)生重大影響。
參考文獻(xiàn)瀏覽器的同源策略
使用indexedDB MDN入門
IndexedDB API參考
W3C IndexedDB 2.0規(guī)范
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/80378.html
摘要:問題描述我自己以擴(kuò)展為基礎(chǔ),封裝了一個異步任務(wù)服務(wù)器框架,數(shù)據(jù)庫操作使用擴(kuò)展,但是在插入數(shù)據(jù)的時候,出現(xiàn)了異常情況,具體狀況如下表引擎的正常插入,沒有問題表引擎查詢都可以的,但是插入失敗,出現(xiàn)回滾現(xiàn)象,字段變化,表的變化,正常返回解決過程監(jiān) 問題描述 我自己以swoole擴(kuò)展為基礎(chǔ),封裝了一個異步任務(wù)服務(wù)器框架,數(shù)據(jù)庫操作使用pdo擴(kuò)展,但是在插入數(shù)據(jù)的時候,出現(xiàn)了異常情況,具體狀況如...
摘要:只要滿足規(guī)范的放入該容器,馬上就會被容器進(jìn)行高效率的管理。根據(jù)康威定律,設(shè)計系統(tǒng)的組織時,最終產(chǎn)生的設(shè)計等價于組織的溝通結(jié)構(gòu),通俗來講,團(tuán)隊的交流機(jī)制應(yīng)該與架構(gòu)分層交互機(jī)制相對應(yīng)。 本博客 貓叔的博客,轉(zhuǎn)載請申明出處 先來看看官網(wǎng)對它的定義。 Java平臺企業(yè)版(Java EE)是社區(qū)驅(qū)動的企業(yè)軟件的標(biāo)準(zhǔn)。Java EE是使用Java Community Process開發(fā)的,其中包括...
閱讀 3163·2021-11-04 16:09
閱讀 3131·2021-09-23 11:49
閱讀 3648·2021-09-09 09:33
閱讀 3632·2021-08-18 10:22
閱讀 2048·2019-08-30 15:55
閱讀 3636·2019-08-30 15:53
閱讀 2662·2019-08-28 18:08
閱讀 898·2019-08-26 18:18