前面已經有提到indexedDB是一個在browser運行的Database,這裡我列出幾項主要的特性:
IndexedDB儲存資料的型式為「
key-value pairs
」。value可以是複雜的結構化object,key可以是這些object的屬性,然後我們可以依object屬性來建立索引,進行資料搜尋以及排序。
IndexedDB是一個基於「
交易性質的資料庫模型 (transactional database model)
」。任何在IndexedDB 上進行的操作都是屬於交易(transaction)。
另外,交易具有生命週期,所以交易結束後又操作交易會引起例外錯誤。而且交易是自動執行生效的,無法手動執行生效。想想看如果一個使用者在兩個頁面開啟同一個網頁時,若是沒有交易機制,這兩個頁面對資料庫的存取操作將會產生衝突,因此交易機制是一個相當有用的機制。
IndexedDB大部分API都是「
非同步
」。IndexedDB API不會回傳資料,相反地,我們需要傳入回呼函數 (Callback function) 。我們並非直接、同步地儲存和取得資料,我們是請求資料庫作業,當作業結束時DOM event再通知我們作業結束,然後我們再透過事件判斷作業成功或失敗。
如果把indexedDB跟傳統關聯式資料庫做個對比的話,IndexedDB中的object store就相當於Relational Database的Table。
不過在我的project中,為了避免使用callback function,我使用了第三方的套件(由
jakearchibald
提供),這讓我能使用promise來去操作indexedDB。(記得將code複製到project中js目錄下,並加入到pre-cache中)
IndexedDB Browser Support
(96%的瀏覽器都支援,其實算相當不錯的)
接下來我就要將用戶的貼文資料(dynamic data)從原本暫存在cache,移植到indexedDB中囉!!
首先要在sw.js中,導入indexedDB的第三方套件。同時為了之後寫的程式碼能重複利用,我將針對indexedDB的操作寫在utility.js中,所以也需要導入:
importScripts('/src/js/idb.js');
importScripts('/src/js/utility.js');
來看一下在utility.js中要怎麼建立indexedDB的object store並將資料寫入:
var dbPromise = idb.open('post-store', 1, function(db) {
if(!db.objectStoreNames.contains('posts')) {
db.createObjectStore('posts', {keyPath: 'id'});
function writeData(objectStore, data) {
return dbPromise.then(function(db) {
var tx = db.transaction(objectStore, 'readwrite'); // first step
var store = tx.objectStore(objectStore); // second step
store.put(data); // third step
return tx.complete;
透過第三方套件,我可以使用idb.open()來建立一個資料庫,需要輸入3個參數(1. 資料庫名稱、2. 資料庫版本[可以隨喜好設定,但不能相同]、3. callback function)。而這個callback function會以建立好的indexedDB object作為輸入,來去操作這個資料庫。
所以在callback function中,我在post-store資料庫中新增一個「名為posts的object store」,而且第二個參數是要去設定在這個object store中,我們要以哪個欄位做為key值。
接著寫要怎麼將firebase的貼文資訊寫入indexedDB呢?事實上操作indexedDB有一個標準執行流程:
開啟資料庫和交易(transaction)。
建立物件存檔(object store)。
發出資料庫操作請求,例如新增(put)或取得(get)資料。
可以看到這三個步驟就是上面writeData() function中的3 step
最後在sw.js中,將從firebase裡fetch回來的資料改成儲存到indexedDB:
// Cache then Network
self.addEventListener('fetch', function(event) {
var url = 'https://trip-diary-f56de.firebaseio.com/posts.json';
// 要修改的地方
if(event.request.url.indexOf(url) > -1) {
event.respondWith(
fetch(event.request).then(function(res) {
var clonedRes = res.clone();
clonedRes.json().then(function(data) {
for(var key in data) {
writeData('posts', data[key]);
return res;
} else if(isInArray(event.request.url, STATIC_FILES)) {
event.respondWith(
caches.match(event.request)
} else {
event.respondWith(
caches.match(event.request).then(function(response) {
if(response) {
return response;
} else {
return fetch(event.request).then(function(res) {
return caches.open(CACHE_DYNAMIC_NAME).then(function(cache) {
// trimCache(CACHE_DYNAMIC_NAME, 20);
cache.put(event.request.url, res.clone());
return res;
}).catch(function(err) {
return caches.open(CACHE_STATIC_NAME).then(function(cache) {
if(event.request.headers.get('accept').includes('text/html')) {
return cache.match('/offline.html');
說明一下,這裡我把firebase fetch回來的response,複製(clone)完後將資料轉成javascript object型式。接著用一個for loop把posts裡面的每一則貼文寫入到"posts" object store中。
來看一下畫面執行的結果吧:
發現我們已經成功地將firebase回傳dynamic data儲存至indexedDB中了,它的key就是我們當初設定的id欄位,value就是貼文的json object。
Day15 結束!! 