indexedDB简单介绍
indexedDB
一、其它幾種前端存儲:
1、cookie
HTTP cookie 通常也叫作 cookie,最初用于在客戶端存儲會話信息。這個規(guī)范要求服務(wù)器在響應(yīng)
HTTP 請求時,通過發(fā)送 Set-Cookie HTTP 頭部包含會話信息。例如, 下面是包含這個頭部的一個 HTTP
響應(yīng) :
這個 HTTP 響應(yīng)會設(shè)置一個名為"name",值為"value"的 cookie。名和值在發(fā)送時都會經(jīng)過 URL
編碼。瀏覽器會存儲這些會話信息,并在之后的每個請求中都會通過 HTTP 頭部 cookie 再將它們發(fā)回服
務(wù)器,比如:
這些發(fā)送回服務(wù)器的額外信息可用于唯一標(biāo)識發(fā)送請求的客戶端。
cookie 是與特定域綁定的。設(shè)置 cookie 后,它會與請求一起發(fā)送到創(chuàng)建它的域。這個限制能保證
cookie 中存儲的信息只對被認(rèn)可的接收者開放,不被其他域訪問。因?yàn)?cookie 存儲在客戶端機(jī)器上,所以為保證它不會被惡意利用,瀏覽器會施加限制。 同時, cookie也不會占用太多磁盤空間。
cookie 在瀏覽器中是由以下參數(shù)構(gòu)成的(這些參數(shù)在 Set-Cookie 頭部中使用分號加空格隔開 ) :
- 名稱:唯一標(biāo)識 cookie 的名稱。 cookie 名不區(qū)分大小寫,因此 myCookie 和 MyCookie 是同一
個名稱。不過,實(shí)踐中最好將 cookie 名當(dāng)成區(qū)分大小寫來對待,因?yàn)橐恍┓?wù)器軟件可能這樣
對待它們。 cookie 名必須經(jīng)過 URL 編碼。 - 值:存儲在 cookie 里的字符串值。這個值必須經(jīng)過 URL 編碼。
- 域: cookie 有效的域。發(fā)送到這個域的所有請求都會包含對應(yīng)的 cookie。這個值可能包含子域(如
www.wrox.com),也可以不包含(如.wrox.com 表示對 wrox.com 的所有子域都有效)。如果不明
確設(shè)置,則默認(rèn)為設(shè)置 cookie 的域。 - 路徑:請求 URL 中包含這個路徑才會把 cookie 發(fā)送到服務(wù)器。例如,可以指定 cookie 只能由
http://www.wrox.com/books/訪問,因此訪問 http://www.wrox.com/下的頁面就不會發(fā)送 cookie,即
使請求的是同一個域。 - 過期時間:表示何時刪除 cookie 的時間戳(即什么時間之后就不發(fā)送到服務(wù)器了)。默認(rèn)情況下,
瀏覽器會話結(jié)束后會刪除所有 cookie。不過,也可以設(shè)置刪除 cookie 的時間。這個值是 GMT 格
式( Wdy, DD-Mon-YYYY HH:MM:SS GMT),用于指定刪除 cookie 的具體時間。這樣即使關(guān)閉
瀏覽器 cookie 也會保留在用戶機(jī)器上。把過期時間設(shè)置為過去的時間會立即刪除 cookie。 - 安全標(biāo)志:設(shè)置之后,只在使用 SSL 安全連接的情況下才會把 cookie 發(fā)送到服務(wù)器。例如,請
求https://www.wrox.com 會發(fā)送 cookie,而請求 http://www.wrox.com 則不會 ;
這個頭部設(shè)置一個名為"name"的 cookie,這個 cookie 在 2007 年 1 月 22 日 7:10:24 過期,對
www.wrox.com 及其他 wrox.com 的子域(如 p2p.wrox.com)有效。
安全標(biāo)志 secure 是 cookie 中唯一的非名/值對,只需一個 secure 就可以了。比如:
HTTP/1.1 200 OK
Content-type: text/html
Set-Cookie: name=value; domain=.wrox.com; path=/; secure
Other-header: other-header-value
這里創(chuàng)建的 cookie 對所有 wrox.com 的子域及該域中的所有頁面有效(通過 path=/指定)。不過,
這個 cookie 只能在 SSL 連接上發(fā)送,因?yàn)樵O(shè)置了 secure 標(biāo)志。
要知道,域、路徑、過期時間和 secure 標(biāo)志用于告訴瀏覽器什么情況下應(yīng)該在請求中包含 cookie。
這些參數(shù)并不會隨請求發(fā)送給服務(wù)器,實(shí)際發(fā)送的只有 cookie 的名/值對 ;
因?yàn)樵?JavaScript 中讀寫 cookie 不是很直觀,所以可以通過輔助函數(shù)來簡化相應(yīng)的操作。與 cookie
相關(guān)的基本操作有讀、寫和刪除。這些在 CookieUtil 對象中表示如下:
2、Storage
Web Storage 的定義了兩個對象: localStorage 和 sessionStorage。 localStorage 是永久存儲機(jī)制, sessionStorage 是跨會話的存儲機(jī)制。這兩種瀏覽器存儲 API 提供了在瀏覽器中不受頁面刷新影響而存儲數(shù)據(jù)的兩種方式。
Storage 類型用于保存名/值對數(shù)據(jù),直至存儲空間上限(由瀏覽器決定)。 Storage 的實(shí)例與其他
對象一樣,但增加了以下方法 :
- clear():刪除所有值;不在 Firefox 中實(shí)現(xiàn)。
- getItem(name):取得給定 name 的值
- key(index):取得給定數(shù)值位置的名稱
- removeItem(name):刪除給定 name 的名/值對
- setItem(name, value):設(shè)置給定 name 的值。
sessionStorage:
因?yàn)?sessionStorage 對象是 Storage 的實(shí)例,所以可以通過使用 setItem()方法或直接給屬
性賦值給它添加數(shù)據(jù)。下面是使用這兩種方式的例子:
// 使用方法存儲數(shù)據(jù)
sessionStorage.setItem("name", "Nicholas");
// 使用屬性存儲數(shù)據(jù)
sessionStorage.book = "Professional JavaScript";
可以使用 getItem()或直接訪問屬性名來取得。下面是使用這兩種方式的例子:
// 使用方法取得數(shù)據(jù)
let name = sessionStorage.getItem("name");
// 使用屬性取得數(shù)據(jù)
let book = sessionStorage.book;
可以結(jié)合 sessionStorage 的 length 屬性和 key()方法遍歷所有的值:
for (let i = 0, len = sessionStorage.length; i < len; i++){let key = sessionStorage.key(i);let value = sessionStorage.getItem(key);alert(`${key}=`${value}`); } for (let key in sessionStorage){let value = sessionStorage.getItem(key);alert(`${key}=${value}`); }要從 sessionStorage 中刪除數(shù)據(jù),可以使用 delete 操作符直接刪除對象屬性,也可以使用
removeItem()方法。下面是使用這兩種方式的例子:
sessionStorage 對象應(yīng)該主要用于存儲只在會話期間有效的小塊數(shù)據(jù)。如果需要跨會話持久存儲
數(shù)據(jù),可以使用 globalStorage 或 localStorage
存儲在 localStorage 中的數(shù)據(jù)會保留到通過 JavaScript 刪除或者用戶清除瀏覽器緩存。 localStorage 數(shù)據(jù)不受頁面刷新影響,也不會因關(guān)閉窗口、標(biāo)簽頁或重新啟動瀏覽器而丟失。
存儲事件
每當(dāng) Storage 對象發(fā)生變化時,都會在文檔上觸發(fā) storage 事件。使用屬性或 setItem()設(shè)置
值、使用 delete 或 removeItem()刪除值,以及每次調(diào)用 clear()時都會觸發(fā)這個事件。這個事件的
事件對象有如下 4 個屬性。
-
domain:存儲變化對應(yīng)的域。
-
key:被設(shè)置或刪除的鍵。
-
newValue:鍵被設(shè)置的新值,若鍵被刪除則為 null。
-
oldValue:鍵變化之前的值。
可以使用如下代碼監(jiān)聽 storage 事件:
window.addEventListener("storage",(event) => alert('Storage changed for ${event.domain}' ));對于 sessionStorage 和 localStorage 上的任何更改都會觸發(fā) storage 事件,但 storage 事
件不會區(qū)分這兩者。
二、indexedDB簡介
IndexedDB和傳統(tǒng)的關(guān)系型數(shù)據(jù)不同的是,它是一個key-value型的數(shù)據(jù)庫。
value可以是復(fù)雜的結(jié)構(gòu)體對象,key可以是對象的某些屬性值也可以是其他的對象(包括二進(jìn)制對象)。你可以使用對象中的任何屬性做為index,以加快查找。
IndexedDB是自帶transaction的,所有的數(shù)據(jù)庫操作都會綁定到特定的事務(wù)上,并且這些事務(wù)是自動提交了,IndexedDB并不支持手動提交事務(wù)。
IndexedDB API大部分都是異步的,在使用異步方法的時候,API不會立馬返回要查詢的數(shù)據(jù),而是返回一個callback。
異步API的本質(zhì)是向數(shù)據(jù)庫發(fā)送一個操作請求,當(dāng)操作完成的時候,會收到一個DOM event,通過該event,我們會知道操作是否成功,并且獲得操作的結(jié)果。
IndexedDB是一種 NoSQL 數(shù)據(jù)庫,和關(guān)系型數(shù)據(jù)庫不同的是,IndexedDB是面向?qū)ο蟮?#xff0c;它存儲的是Javascript對象。
IndexedDB還有一個很重要的特點(diǎn)是其同源策略,每個源都會關(guān)聯(lián)到不同的數(shù)據(jù)庫集合,不同源是不允許訪問其他源的數(shù)據(jù)庫,從而保證了IndexedDB的安全性。
三、使用原因:開發(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。
四、indexedDB的相關(guān)操作
// 假如一開始有這樣的數(shù)據(jù)需要存儲 let data = [{id: 1,name: "lzc",age: 18,sex: "man",addTime: "2021-2-1"},{id: 2,name: "cb",age: 19,sex: "man",addTime: "2021-2-2"},{id: 3,name: "kj",age: 20,sex: "woman",addTime: "2021-2-3"},{id: 4,name: "juanjuan",age: 21,sex: "woman",addTime: "2021-2-4"} ]1、創(chuàng)建或者打開數(shù)據(jù)庫
/* 不同的瀏覽器對于IndexedDB有不同的實(shí)現(xiàn),正常來說,我們可以使用window.indexedDB來獲取到瀏覽器的indexedDB對象。但是對于某些瀏覽器來說,還沒有使用標(biāo)準(zhǔn)的window.indexedDB,而是用帶前綴的實(shí)現(xiàn)使用IndexedDB第一步,就是創(chuàng)建或打開一個數(shù)據(jù)庫。我們使用window.indexedDB.open(DBName)這個API來打進(jìn)行操作。 */ window.indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB;if (!window.indexedDB) {console.log("Your browser doesn't support a stable version of IndexedDB. Such and such feature will not be available."); } else {const request = window.indexedDB.open('people');request.onupgradeneeded = function (event) {}request.onsuccess = function(event) {console.log(request === event.target) // true}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事件。
2、更新數(shù)據(jù)庫版本號
window.indexedDB.open的第二個參數(shù)即為版本號。在不指定的情況下,默認(rèn)版本號為1。
const request = window.indexedDB.open('people', 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)。
3、存儲空間操作
我們使用createObjectStore來創(chuàng)建一個存儲空間。同時,使用createIndex來創(chuàng)建它的索引。
var request = window.indexedDB.open('people', 1);request.onupgradeneeded = function (event) {console.log(request === event.target) // truevar db = event.target.result;var objectStore = db.createObjectStore('user', {keyPath: 'id', autoIncrement: true});// 可以循環(huán)遍歷,給對象數(shù)組或者對象的每個鍵都生成索引(每個索引里面的數(shù)據(jù)項(xiàng)的排序按索引的升序來排列,索引沒有順序可言,就按原始順序)if (Object.prototype.toString.call(data) === "[object Array]" && data.length) {for (let i in data[0]) {objectStore.createIndex(i, i, { unique: false })}} else if (Object.prototype.toString.call(data) === "[object object]") {for (let i in data) {objectStore.createIndex(i, i, { 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ù):
而createIndex能夠給當(dāng)前的存儲空間設(shè)置一個索引。它接受三個參數(shù):
4、數(shù)據(jù)操作
1、事務(wù)transaction
在IndexedDB中,我們使用事務(wù)transaction來進(jìn)行數(shù)據(jù)庫的操作。事務(wù)有三個模式(常量已經(jīng)棄用)
- readOnly:只讀。
- readwrite:讀寫。
- versionchange:數(shù)據(jù)庫版本變化。
我們創(chuàng)建一個事務(wù)時,需要從上面選擇一種模式,如果不指定的話,則默認(rèn)為只讀模式。
const transaction = db.transaction(['user'], 'readwrite');事務(wù)函數(shù)transaction的第一個參數(shù)為需要關(guān)聯(lián)的存儲空間,第二個可選參數(shù)為事務(wù)模式。與上面類似,事務(wù)成功時也會觸發(fā)onsuccess函數(shù),失敗時觸發(fā)onerror函數(shù)。
2、增加數(shù)據(jù)
當(dāng)存儲空間objectStore初始化完成后,我們可以把數(shù)據(jù)放入存儲空間中??梢允褂?add()或 put()寫入數(shù)據(jù)。這兩個方法都接收一個參數(shù), 即要存儲的對象,并把對象保存到對象存儲。這兩個方法只在對象存儲中已存在同名的鍵時有區(qū)別。這 種情況下,add()會導(dǎo)致錯誤,而 put()會簡單地重寫該對象。
var request = window.indexedDB.open('people', 1);request.onsuccess = function (event) {var db = event.target.result;var transaction = db.transaction(['user'], 'readwrite');var objectStore = transaction.objectStore('user');for (let item of data) {objectStore.add(item);} }注:add方法中的第二個參數(shù)key值是指定存儲空間中的keyPath值,如果data中包含keyPath值或者此值為自增值,那么可以略去此參數(shù)。
// 1、createObjectStore第二個參數(shù)里加 { autoIncrement: true },這個時候key使用從1開始的自增數(shù),遍歷出來的數(shù)據(jù)和原始要存儲的數(shù)據(jù)順序一致; // 2、objectStore.add(item, item.name); 指定了keyPath值,這時候遍歷出來的數(shù)據(jù)順序,可能和原始的不一樣,這里是按照keyPath值字母語序來排列的;這里寫不寫{ autoIncrement: true }都可以; // 3、createObjectStore第二個參數(shù)里加 { keyPath: "name" },這里不能再在objectStore.add(item)里面添加第二個參數(shù),即使這里第二個參數(shù)添加的和createObjectStore里面的第二個參數(shù)的keyPath的一致;上面的只影響user對象存儲里面的排序,其它索引的排序只和自己的索引有關(guān);
3、查找數(shù)據(jù)
1、通過特定值獲取數(shù)據(jù)(只能獲取單條數(shù)據(jù))
- 這里默認(rèn)只能獲取通過createObjectStore的第二參數(shù)的keyPath設(shè)置的參數(shù)的user對象存儲的數(shù)據(jù),其它索引的無法查出;
- 或者使用index方法改變默認(rèn)索引,比如let store = objectStore.index(“age”),再用store.get(18)就可以通過age來查詢了(前提是用createIndex創(chuàng)建了age索引的對象存儲,才能使用objectStore.index(“age”),才能查詢);
2、通過游標(biāo)獲取數(shù)據(jù)(獲取多條數(shù)據(jù))
當(dāng)你需要遍歷整個存儲空間中的數(shù)據(jù)時,你就需要使用到游標(biāo),與傳統(tǒng)數(shù)據(jù)庫查詢不同,游標(biāo)不會事先收集所有結(jié)果。相反,游標(biāo)指向 第一個結(jié)果,并在接到指令前不會主動查找下一條數(shù)據(jù)
var request = window.indexedDB.open('people', 1);request.onsuccess = function (event) {var db = event.target.result;var transaction = db.transaction(['user'], 'readwrite');var objectStore = transaction.objectStore('user');var request = objectStore.openCursor();request.onsuccess = function (event) {var cursor = event.target.result;if (cursor) {console.log(cursor.key, cursor.value);cursor.continue();}};request.onerror = function (event) {// 錯誤處理!}; }使用游標(biāo)時有一個需要注意的地方,當(dāng)游標(biāo)遍歷整個存儲空間但是并未找到給定條件的值時,仍然會觸發(fā)onsuccess函數(shù)。
openCursor和openKeyCursor有兩個參數(shù):**openKeyCursor**遍歷出來的值里面沒有value對象值,其它和openCursor一致
- next,從前往后獲取所有數(shù)據(jù)(重復(fù)值也會遍歷出來)
- prev,從后往前獲取所有數(shù)據(jù)(重復(fù)值也會遍歷出來)
- nextunique,從前往后獲取數(shù)據(jù)(重復(fù)數(shù)據(jù)只取第一條,索引重復(fù)即認(rèn)為重復(fù),下同)
- prevunique,從后往前獲取數(shù)據(jù)(重復(fù)數(shù)據(jù)只取第一條)
4、使用索引
在前面構(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(['user'], 'readwrite');var objectStore = transaction.objectStore('user');// 使用索引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.key);} }5、修改數(shù)據(jù)===>修改時,須帶上這條數(shù)據(jù)的索引鍵
注:put方法不僅能夠修改現(xiàn)有數(shù)據(jù),也能夠往存儲空間中增加新的數(shù)據(jù)。
使用objectStore.put方法時,參數(shù)為已存在的鍵,即為修改,為不存在的鍵時,即為增加;
這里不能像查詢一樣,通過store.index()來修改索引,index上只有g(shù)et方法
var transaction = db.transaction(['user'], 'readwrite'); var store = transaction.objectStore("user");// 用put增加一條新數(shù)據(jù) var request1 = store.put({id: 5,name: "李志聰",age: 22,sex: "man",addTime: "2021-2-5" });// 用put增加一條新數(shù)據(jù),改sex描述===>是修改時,須帶上這條數(shù)據(jù)的索引鍵 var request2 = store.put({id: 2,name: "lzc",age: 18,sex: "男",addTime: "2021-2-2" });request1.onsuccess = function (event) {}6、刪除數(shù)據(jù)
var transaction = db.transaction(['user'], 'readwrite'); var store = transaction.objectStore("user");var request = store.delete(對應(yīng)某一條keyPath值);request.onsuccess = function (event) {}7、關(guān)閉數(shù)據(jù)庫
// 比如在destoryed鉤子里面可以關(guān)閉數(shù)據(jù)庫 db.close();五、安全相關(guān)
IndexedDB也受到瀏覽器同源策略的限制。
六、dexie.js庫的使用
dexie.js:A Minimalistic Wrapper for IndexedDB
基于indexDB的Dexie數(shù)據(jù)庫
// 下面配一個簡單的演示// 創(chuàng)建一個數(shù)據(jù)庫 若數(shù)據(jù)庫已存在則為打開 // 打開數(shù)據(jù)庫時,會判斷當(dāng)前version值是否大于已經(jīng)存在的version值,若大于則會upgrade即升到最高版本 var db = new Dexie("mydb");// 設(shè)定版本,添加一個person表,里面有自增的id主鍵,有name和age兩個索引 // 注意:不要像在SQL中那樣聲明所有列。只聲明要索引的屬性,即要在where(…)查詢中使用的屬性。 db.version(1).stores({person: "++id, name, age" })// 增加(還是使用上面的數(shù)據(jù)進(jìn)行存儲) for(let item of data) {db.person.add(item) }// 修改 setTimeout(() => {db.person.put({id: 2,name: "聰波",age: 18,sex: "boy",addTime: "2021-2-2"})console.log(db.person.get(2)) // 一個promise對象// 普通查詢db.person.get(2).then(res => {console.log("查詢的結(jié)果為===>", res)}) }, 1000)// 帶條件查詢 setTimeout(() => {// adove、aboveOrEqual、below、belowOrEqual、between、equalsdb.person.where("age").aboveOrEqual(18).toArray().then(res => {console.log("年齡大于等于18歲的有===>", res)}) }, 2000)// 刪除 setTimeout(() => {// 刪除第一條db.person.delete(1);db.person.get(1).then(res => {console.log("第一條數(shù)據(jù)===>", res)}) }, 3000)setTimeout(() => {db.close();console.log("要是數(shù)據(jù)庫沒有關(guān)閉,下面將打印第三天數(shù)據(jù)");db.person.get(3).then(res => {console.log("第三條數(shù)據(jù)===>", res)}) }, 4000)總結(jié)
以上是生活随笔為你收集整理的indexedDB简单介绍的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 机器人运动估计系列(二)——运动学方程(
- 下一篇: 热爱生活,更热爱代码