iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 13
1
Modern Web

你應該要知道的新一代Web技術---漸進式網頁(PWA)系列 第 13

[Day13] Service Worker進階Caching實作(Part3)

回顧一下昨天,我根據不同的request類型而使用不同的cache strategies:

  • "https://httpbin.org/get" :只要是去外部server獲取用戶貼文資料的request,為了要給用戶在有網路情形下更快速的使用者體驗,我是用「cache then network strategies」。

當然目前server是假的,之後我會再建一個server來存放PWA中的用戶貼文資訊。

  • "其他request" :像是要獲取App shell的靜態資源或是其他動態資源,我則是使用「cache with network fallback strategies」,這樣即使離線,用戶也可以正常瀏覽我的PWA。

接著我還想再針對App shell中的靜態資源使用「cache only strategies」,因為這些靜態資源其實都不太會更新變動,就算更新了由於我pre-caching是寫在service worker的install階段,所以每次更改完靜態資源的程式碼後,service worker都會在重新install and activate。這樣static cache的版本也會順帶更新了。

所以針對這些資源我想非常適合不須透過網路直接從cache取得的策略。我直接在之前if-else中再加入一層判斷是否request url是我預先設定好的pre-cache靜態資源(是的話我希望只要從cache中取得就好):

// Application shell
var STATIC_FILES = [
    '/',
    '/index.html',
    '/offline.html',
    '/src/js/app.js',
    '/src/js/feed.js',
    '/src/js/promise.js',
    '/src/js/fetch.js',
    '/src/js/material.min.js',
    '/src/css/app.css',
    '/src/css/feed.css',
    '/src/images/main-image.jpg',
    'https://fonts.googleapis.com/css?family=Noto+Sans+TC&display=swap',
    'https://fonts.googleapis.com/icon?family=Material+Icons',
    'https://cdnjs.cloudflare.com/ajax/libs/material-design-lite/1.3.0/material.indigo-pink.min.css'
]
self.addEventListener('fetch', function(event) {
    var url = 'https://httpbin.org/get';

    if(event.request.url.indexOf(url) > -1) {
        // Cache then Network
        event.respondWith(
            caches.open(CACHE_DYNAMIC_NAME).then(function(cache) {
                return fetch(event.request).then(function(res) {
                    cache.put(event.request, res.clone());
                    return res;
                });
            })
        );
    } else if(isInArray(event.request.url, STATIC_FILES)) {   // 新增的判斷式
        // Cache Only
        event.respondWith(
            caches.match(event.request)
        );
    } else {
        // Cache with Network Fallback
        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) {
                            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');
                            }
                        });
                    });
                }
            })
        );
    }
});

我這裡又另外寫了一個isInArray() function來判斷輸入的url是否存在於STATIC_FILES陣列中。

function isInArray(string, array) {
    var cachePath;
    if (string.indexOf(self.origin) === 0) {   // request的domain是否與我們PWA的domain相同
      console.log('matched ', string);
      cachePath = string.substring(self.origin.length);   // 將domain之後的url擷取出來,也就是localhost:8080之後的url
    } else {
      cachePath = string;   // 儲存完整的request(也就是外部的CDNs)
    }
    return array.indexOf(cachePath) > -1;
}

另外我還做了一個小小的improvement,可以看到在前面我的strategy code中當fetch request發生網路錯誤時,會跳到catch exception裡。而裡面我們可以在去判斷如果今天的request希望回傳的response是一個html網頁的話,就回傳offline.html。


我們可以發現的在PWA中dymanic cache隨著網站功能越來越多,暫存的東西也會越來越多。因此為了避免塞爆瀏覽器的cache,我這裡又寫了一個function來「限制dymanic cache可容納的最大筆數」:

function trimCache(cacheName, maxItems) {
    caches.open(cacheName).then(function(cache) {
        return cache.keys().then(function(keys) {
            if(keys.length > maxItems) {
                cache.delete(keys[0]).then(trimCache(cacheName, maxItems));
            }
        });
    })
}

接著在我前面寫的cache strategies中,每次要put新request到dynamic cache之前,先執行trimCache() function。

trimCache(CACHE_DYNAMIC_NAME, 20);   // 以我的PWA project來說,較合理的筆數大約是20筆
cache.put(event.request, res.clone());

cache大致上就介紹到這邊,接下來是要來練習如何使用browser中的indexedDB來儲存我PWA project中的動態資料(Dynamic Data)

Day13 結束!! /images/emoticon/emoticon07.gif


上一篇
[Day12] Service Worker進階Caching實作(Part2)
下一篇
[Day14] 使用indexedDB暫存Dynamic Data(Part1)
系列文
你應該要知道的新一代Web技術---漸進式網頁(PWA)29
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言