相关文章推荐
飘逸的四季豆  ·  记一次Razor ...·  1 年前    · 
深情的豌豆  ·  gem i ...·  1 年前    · 
前面已經有提到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中。

    來看一下畫面執行的結果吧:/images/emoticon/emoticon16.gif
    發現我們已經成功地將firebase回傳dynamic data儲存至indexedDB中了,它的key就是我們當初設定的id欄位,value就是貼文的json object。

    Day15 結束!! /images/emoticon/emoticon29.gif