Progressive Web Apps(PWA)核心技術(shù)-Indexed DB

在使用的過(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

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市究孕,隨后出現(xiàn)的幾起案子啥酱,更是在濱河造成了極大的恐慌,老刑警劉巖厨诸,帶你破解...
    沈念sama閱讀 222,000評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件镶殷,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡微酬,警方通過(guò)查閱死者的電腦和手機(jī)绘趋,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,745評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén)颤陶,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人陷遮,你說(shuō)我怎么就攤上這事滓走。” “怎么了帽馋?”我有些...
    開(kāi)封第一講書(shū)人閱讀 168,561評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵搅方,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我绽族,道長(zhǎng)姨涡,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,782評(píng)論 1 298
  • 正文 為了忘掉前任吧慢,我火速辦了婚禮涛漂,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘检诗。我一直安慰自己匈仗,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,798評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布岁诉。 她就那樣靜靜地躺著锚沸,像睡著了一般。 火紅的嫁衣襯著肌膚如雪涕癣。 梳的紋絲不亂的頭發(fā)上哗蜈,一...
    開(kāi)封第一講書(shū)人閱讀 52,394評(píng)論 1 310
  • 那天,我揣著相機(jī)與錄音坠韩,去河邊找鬼距潘。 笑死,一個(gè)胖子當(dāng)著我的面吹牛只搁,可吹牛的內(nèi)容都是我干的音比。 我是一名探鬼主播,決...
    沈念sama閱讀 40,952評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼氢惋,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼洞翩!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起焰望,我...
    開(kāi)封第一講書(shū)人閱讀 39,852評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤骚亿,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后熊赖,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體来屠,經(jīng)...
    沈念sama閱讀 46,409評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,483評(píng)論 3 341
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了俱笛。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片捆姜。...
    茶點(diǎn)故事閱讀 40,615評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖迎膜,靈堂內(nèi)的尸體忽然破棺而出泥技,到底是詐尸還是另有隱情,我是刑警寧澤星虹,帶...
    沈念sama閱讀 36,303評(píng)論 5 350
  • 正文 年R本政府宣布零抬,位于F島的核電站,受9級(jí)特大地震影響宽涌,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜蝶棋,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,979評(píng)論 3 334
  • 文/蒙蒙 一卸亮、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧玩裙,春花似錦兼贸、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,470評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)决侈。三九已至,卻和暖如春赖歌,著一層夾襖步出監(jiān)牢的瞬間枉圃,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,571評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工庐冯, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留孽亲,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,041評(píng)論 3 377
  • 正文 我出身青樓展父,卻偏偏與公主長(zhǎng)得像返劲,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子栖茉,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,630評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容