在使用的過(guò)程中我們通常使用cache緩存html裳涛、css叁丧、js等文件信息襟雷,但是一些特殊的數(shù)據(jù)需要我們借助數(shù)據(jù)庫(kù)的支持捺信,這里推薦使用IndexedDB酌媒。
IndexedDB是一個(gè)大型的noSQL存儲(chǔ)系統(tǒng)欠痴。 它使您可以在用戶的瀏覽器中存儲(chǔ)任何內(nèi)容。 除了通常的搜索秒咨,獲取和存儲(chǔ)操作之外喇辽,IndexedDB還支持事務(wù)。
檢查瀏覽器是否支持IndexedDB
if (!('indexedDB' in window)) {
console.log('This browser doesn\'t support IndexedDB');
return;
}
創(chuàng)建數(shù)據(jù)庫(kù)
使用IndexedDB我們可以創(chuàng)建多個(gè)數(shù)據(jù)庫(kù)雨席,但是原則上我們每個(gè)應(yīng)用程序只需要建立一個(gè)數(shù)據(jù)庫(kù)菩咨。
name:數(shù)據(jù)庫(kù)名稱,version:版本號(hào)陡厘,upgradeCallback:回調(diào)方法
idb.open(name, version, upgradeCallback)
IndexedDB之對(duì)象存儲(chǔ)
主要通過(guò)創(chuàng)建數(shù)據(jù)庫(kù)是回調(diào)返回的實(shí)例調(diào)用createObjectStore方法來(lái)創(chuàng)建對(duì)象存儲(chǔ)抽米。
if (!('indexedDB' in window)) {
console.log('This browser doesn\'t support IndexedDB');
return;
}
var dbPromise = idb.open('test-db2', 1, function(upgradeDb) {
console.log('making a new object store');
if (!upgradeDb.objectStoreNames.contains('firstOS')) {
upgradeDb.createObjectStore('firstOS');
}
});
- 定義主鍵并設(shè)置自增
使用keyPath定義主鍵,使用autoIncrement設(shè)置主鍵自增糙置。
var dbPromise = idb.open('test-db3', 1, function(upgradeDb) {
if (!upgradeDb.objectStoreNames.contains('logs')) {
upgradeDb.createObjectStore('logs', {keyPath: 'id', autoIncrement: true});
}
});
- 定義索引
創(chuàng)建索引云茸,在對(duì)象存儲(chǔ)實(shí)例上調(diào)用createIndex方法:
objectStore.createIndex('indexName','property'谤饭,options);
這個(gè)方法創(chuàng)建并返回一個(gè)索引對(duì)象标捺。 createIndex將新索引的名稱作為第一個(gè)參數(shù),第二個(gè)參數(shù)指向要索引的數(shù)據(jù)的屬性揉抵。最后一個(gè)參數(shù)可以讓你定義兩個(gè)選項(xiàng)來(lái)決定索引如何工作:unique和multiEntry亡容。如果unique設(shè)置為true,則索引不允許單個(gè)鍵的重復(fù)值冤今。當(dāng)索引屬性是一個(gè)數(shù)組時(shí)闺兢,multiEntry決定了createIndex的行為。如果設(shè)置為true辟汰,則createIndex將在每個(gè)數(shù)組元素的索引中添加一個(gè)條目列敲。否則,它會(huì)添加一個(gè)包含該數(shù)組的條目帖汞。
var dbPromise = idb.open('test-db4', 1, function(upgradeDb) {
if (!upgradeDb.objectStoreNames.contains('people')) {
var peopleOS = upgradeDb.createObjectStore('people', {keyPath: 'email'});
peopleOS.createIndex('gender', 'gender', {unique: false});
peopleOS.createIndex('ssn', 'ssn', {unique: true});
}
});
注意:每次將數(shù)據(jù)寫(xiě)入?yún)⒖紝?duì)象庫(kù)時(shí)戴而,索引都會(huì)更新。 索引越多意味著IndexedDB的工作量越大翩蘸。
IndexedDB之操作數(shù)據(jù)
IndexedDB中的所有數(shù)據(jù)操作都是在一個(gè)事務(wù)中執(zhí)行的所意。 每個(gè)操作都有這樣的形式:
1.獲取數(shù)據(jù)庫(kù)對(duì)象
2.在數(shù)據(jù)庫(kù)上打開(kāi)事務(wù)
3.在事務(wù)上打開(kāi)對(duì)象存儲(chǔ)
4.在對(duì)象存儲(chǔ)上執(zhí)行操作
寫(xiě)入數(shù)據(jù)
要?jiǎng)?chuàng)建數(shù)據(jù),請(qǐng)?jiān)趯?duì)象存儲(chǔ)上調(diào)用add方法催首,并傳入要添加的數(shù)據(jù)扶踊。 Add有一個(gè)可選的第二個(gè)參數(shù),它可以讓你在創(chuàng)建時(shí)為單個(gè)對(duì)象定義主鍵郎任,但是只有當(dāng)你沒(méi)有在createObjectStore中指定關(guān)鍵路徑時(shí)才能使用它秧耗。
someObjectStore.add(data, optionalKey);
dbPromise.then(function(db) {
var tx = db.transaction('store', 'readwrite');//開(kāi)啟讀寫(xiě)事物
var store = tx.objectStore('store');
var item = {
name: 'sandwich',
price: 4.99,
description: 'A very tasty sandwich',
created: new Date().getTime()
};
store.add(item);
return tx.complete;
}).then(function() {
console.log('added item to the store os!');
});
讀取數(shù)據(jù)
要讀取數(shù)據(jù),請(qǐng)調(diào)用對(duì)象存儲(chǔ)上的get方法舶治。 get方法使用要從存儲(chǔ)中檢索的對(duì)象的主鍵分井。
someObjectStore.get(primaryKey);
dbPromise.then(function(db) {
var tx = db.transaction('store', 'readonly');//開(kāi)啟只讀事物
var store = tx.objectStore('store');
return store.get('sandwich');//如果沒(méi)有該值則返回undefined
}).then(function(val) {
console.dir(val);
});
更新數(shù)據(jù)
要更新數(shù)據(jù)车猬,請(qǐng)?jiān)趯?duì)象存儲(chǔ)上調(diào)用put方法。 put方法與add方法非常相似尺锚,可以用來(lái)代替add來(lái)在對(duì)象存儲(chǔ)中創(chuàng)建數(shù)據(jù)珠闰。 像add一樣,put一個(gè)數(shù)據(jù)和一個(gè)可選的主鍵:
someObjectStore.put(data, optionalKey);
dbPromise.then(function(db) {
var tx = db.transaction('store', 'readwrite');
var store = tx.objectStore('store');
var item = {
name: 'sandwich',
price: 99.99,
description: 'A very tasty, but quite expensive, sandwich',
created: new Date().getTime()
};
store.put(item);
return tx.complete;
}).then(function() {
console.log('item updated!');
});
刪除數(shù)據(jù)
要?jiǎng)h除數(shù)據(jù)瘫辩,在存儲(chǔ)對(duì)象上調(diào)用delete方法伏嗜。
someObjectStore.delete(primaryKey);
dbPromise.then(function(db) {
var tx = db.transaction('store', 'readwrite');
var store = tx.objectStore('store');
store.delete(key);
return tx.complete;
}).then(function() {
console.log('Item deleted');
});
獲取所有數(shù)據(jù)
我們還可以使用getAll方法或使用游標(biāo)從對(duì)象存儲(chǔ)或索引中檢索所有數(shù)據(jù)(或子集)。
- 使用getAll方法
此方法返回與指定的鍵或鍵范圍匹配的對(duì)象存儲(chǔ)中的所有對(duì)象伐厌。
someObjectStore.getAll(optionalConstraint);
dbPromise.then(function(db) {
var tx = db.transaction('store', 'readonly');
var store = tx.objectStore('store');
return store.getAll();
}).then(function(items) {
console.log('Items by name:', items);
});
- 使用游標(biāo)
另一種檢索所有數(shù)據(jù)的方法是使用游標(biāo)承绸。 游標(biāo)會(huì)逐個(gè)選擇對(duì)象存儲(chǔ)或索引中的每個(gè)對(duì)象,讓您在選擇數(shù)據(jù)時(shí)對(duì)其執(zhí)行操作弧械。
someObjectStore.openCursor(optionalKeyRange, optionalDirection);
dbPromise.then(function(db) {
var tx = db.transaction('store', 'readonly');
var store = tx.objectStore('store');
return store.openCursor();
}).then(function logItems(cursor) {
if (!cursor) {
return;
}
console.log('Cursored at:', cursor.key);
for (var field in cursor.value) {
console.log(cursor.value[field]);
}
return cursor.continue().then(logItems);
}).then(function() {
console.log('Done cursoring');
});
這個(gè)方法返回一個(gè)promise八酒,用游標(biāo)對(duì)象表示對(duì)象存儲(chǔ)中的第一個(gè)對(duì)象,如果沒(méi)有對(duì)象刃唐,則返回undefined羞迷。 要移動(dòng)到對(duì)象存儲(chǔ)中的下一個(gè)對(duì)象,我們調(diào)用cursor.continue画饥。
我們首先獲取數(shù)據(jù)庫(kù)對(duì)象衔瓮,創(chuàng)建一個(gè)事務(wù),并打開(kāi)一個(gè)對(duì)象存儲(chǔ)抖甘。我們?cè)趯?duì)象存儲(chǔ)上調(diào)用openCursor方法热鞍,并將游標(biāo)對(duì)象傳遞給.then中的回調(diào)函數(shù)。這次我們將回調(diào)函數(shù)命名為“l(fā)ogItems”衔彻,所以我們可以從函數(shù)內(nèi)部調(diào)用它并進(jìn)行循環(huán)薇宠。 if(!cursor){return;}如果store.openCursor()返回的promise被解析為undefined艰额,或者cursor.continue()返回的promise被解析為undefined(表示沒(méi)有更多的對(duì)象)澄港。
游標(biāo)對(duì)象包含一個(gè)代表項(xiàng)目主鍵的鍵屬性。它還包含一個(gè)代表數(shù)據(jù)的值屬性柄沮。在logItems結(jié)束時(shí)回梧,我們返回cursor.continue().then(logItems)。 cursor.continue方法返回一個(gè)promise祖搓,該promise解析為表示store中下一個(gè)store的游標(biāo)對(duì)象狱意,如果沒(méi)有更多的對(duì)象,則返回undefined拯欧。這個(gè)結(jié)果被傳遞到.then中的回調(diào)函數(shù)中详囤,我們選擇把logItems作為這個(gè)函數(shù),這樣函數(shù)就會(huì)循環(huán)镐作。因此纬纪,logItems繼續(xù)調(diào)用自己蚓再,直到?jīng)]有對(duì)象保留。
通過(guò)索引獲取某個(gè)范圍內(nèi)數(shù)據(jù)
我們可以通過(guò)不同的方式獲得所有的數(shù)據(jù)包各,但是如果我們只需要基于某個(gè)特定屬性的數(shù)據(jù)子集呢? 這時(shí)我們可以使用索引靶庙。索引讓我們通過(guò)主鍵以外的屬性獲取對(duì)象存儲(chǔ)中的數(shù)據(jù)问畅。 我們可以在任何屬性上創(chuàng)建索引,在該屬性上指定范圍六荒,并使用getAll方法或游標(biāo)獲取范圍內(nèi)的數(shù)據(jù)护姆。
我們使用IDBKeyRange對(duì)象來(lái)定義范圍。 這個(gè)對(duì)象有四個(gè)方法來(lái)定義范圍的限制:upperBound掏击,lowerBound卵皂,bound(這意味著兩者),only砚亭。 upperBound和lowerBound方法指定范圍的上限和下限灯变。
IDBKeyRange.lowerBound(indexKey); IDBKeyRange.upperBound(indexKey); IDBKeyRange.bound(lowerIndexKey, upperIndexKey); IDBKeyRange.only(indexKey);
function searchItems(lower, upper) {
if (lower === '' && upper === '') {return;}
var range;
if (lower !== '' && upper !== '') {
range = IDBKeyRange.bound(lower, upper);
} else if (lower === '') {
range = IDBKeyRange.upperBound(upper);
} else {
range = IDBKeyRange.lowerBound(lower);
}
dbPromise.then(function(db) {
var tx = db.transaction(['store'], 'readonly');
var store = tx.objectStore('store');
var index = store.index('price');//打開(kāi)索引
return index.openCursor(range);
}).then(function showRange(cursor) {
if (!cursor) {return;}
console.log('Cursored at:', cursor.key);
for (var field in cursor.value) {
console.log(cursor.value[field]);
}
return cursor.continue().then(showRange);
}).then(function() {
console.log('Done cursoring');
});
}
使用數(shù)據(jù)庫(kù)版本號(hào)
var dbPromise = idb.open('test-db7', 3, function(upgradeDb) {
switch (upgradeDb.oldVersion) {
case 0:
upgradeDb.createObjectStore('store', {keyPath: 'name'});
case 1:
var storeOS = upgradeDb.transaction.objectStore('store');
storeOS.createIndex('price', 'price');
case 2:
var storeOS = upgradeDb.transaction.objectStore('store');
storeOS.createIndex('description', 'description');
}
});
如果瀏覽器中沒(méi)有該數(shù)據(jù)庫(kù),那么oldVersion將為0捅膘,從case:0開(kāi)始添祸,依次執(zhí)行0、1寻仗、2刃泌,創(chuàng)建主鍵為name的存儲(chǔ)對(duì)象,然后為該對(duì)象創(chuàng)建一個(gè)price的索引和一個(gè)description的索引署尤。
如果瀏覽器中存在該數(shù)據(jù)庫(kù)耙替,并且已經(jīng)創(chuàng)建了一個(gè)名為price的索引,那么它的oldVersion為2曹体,則0俗扇,1將被跳過(guò),直接創(chuàng)建一個(gè)名為description的索引混坞。
注意:這里的case沒(méi)有break狐援;
參考鏈接:
https://developers.google.com/web/ilt/pwa/working-with-indexeddb#getting_all_the_data