iT邦幫忙

2018 iT 邦幫忙鐵人賽
DAY 29
0
Modern Web

30天走訪Progressive Web Apps(PWAs)系列 第 29

Day29-Workbox Tool

Workbox是Google開發的一個工具,可以簡單透過幾個指令產生Service Worker檔案
,方便開發者管理緩存機制,也提高開發者的開發效率。

安裝

npm install --save-dev workbox-cli

設定指令

打開package.json

"scripts": {
    ...
    "generate-sw": "workbox generate:sw"
},

新增一組指令碼generate-sw
現在就可以透過npm run generate-sw產生Service worker的檔案。

執行 npm run generate-sw

https://ithelp.ithome.com.tw/upload/images/20180114/20103808dY4SdLGFah.png

  1. 設定網站根目錄
  2. 要快取的副檔名
  3. Service Worker建置的路徑和檔名(注意不要取跟我們設定的service-worker.js同名不然會被取代)
  4. 要不要將這些設定儲存到設定檔

接著,就會自動產生Service worker的檔案了。
打開產生的Service Worker檔案

const fileManifest = [
  {
    "url": "404.html",
    "revision": "a4e2271d19eb1f6f93a15e1b7a4e74dd"
  },
  ...
]

會看到根目錄底下,所有的資源都存在fileManifest的物件中,revision後面則是hash過的字串。

拉到最下面

const workboxSW = new self.WorkboxSW();
workboxSW.precache(fileManifest);

這邊建立一個Workbox Service Worker並且透過precache()將物件的資源全部快取起來,這就跟我們剛開始學快取的時候,載入網頁預先存取的快取效果一樣。

但現在產生的檔案是將所有資源都快取起來,這並不是我們期望的,
因此我們可以來改workbox-cli-config.js,當再跑產生檔案時,會根據這檔案設定的內容去產生Service Worker,上面我們選擇的設定都可以在此檔案中找到。

module.exports = {
  "globDirectory": "public\\",
  "globPatterns": [
    "**/*.{html,ico,json,js,css,JPG,PNG,png}"
  ],
  "swDest": "public/workbox-sw-v1.js",
  "globIgnores": [
    "..\\workbox-cli-config.js"
  ]
};

globDirectory是網站根目錄的位置
globPatterns內容放要快取的檔案格式
swDest檔案輸出的位置
globIgnores不放入快取名單的資源

設定客製預先快取的內容

module.exports = {
  "globDirectory": "public\\",
  "globPatterns": [
    "**/*.{html,ico,json,js,css}",
    "src/images/*.{jpg,JPG,png,PNG}"
  ],
  "swDest": "public/workbox-sw-v1.js",
  "globIgnores": [
    "..\\workbox-cli-config.js",
    "aboutUs/**"
  ]
};

稍微修改了一下globPatterns,原本會尋訪所有網站資料夾底下所有的圖檔,現在限定在src/images/資料夾底下的圖檔才快取
另外,globIgnores中加入aboutUs/資料夾底下的內容先不快取起來。

這時候問題來了,
假如原本Service Worker裡面有寫很多監聽事件,那麼現在透過設定檔產生新的Service Worker,好不容易寫好的程式不就被覆蓋了嗎?

將常用的Service Worker程式抽離

Step1: 建立新的檔案

在此建立service-worker-base.js命名隨意,這檔案會放置我們不想被覆蓋的程式。

Step2: 修改package.json

原本指令是使用workbox generate:sw,但此方法會產生一個新的檔案,
因此要改成inject:manifest藉由注入的方式合併檔案。

  "scripts": {
    ...
    "generate-sw": "workbox inject:manifest"
  }

Step3: 修改service-worker-base.js

importScripts('workbox-sw.prod.v2.1.2.js');

const workboxSW = new self.WorkboxSW();
workboxSW.precache([]);

將自動產生的service worker檔案中,使用到的檔案,和precache語法貼過來。

Step4: 修改config檔

module.exports = {
  ...
  "swSrc": "public/service-worker-base.js",
  ...
};

設定swSrc告知要注入的檔案是哪一個。

Step5: npm run generate-sw

importScripts('workbox-sw.prod.v2.1.2.js');

const workboxSW = new self.WorkboxSW();
workboxSW.precache([
  {
    "url": "404.html",
    "revision": "a4e2271d19eb1f6f93a15e1b7a4e74dd"
  }
  ]);

執行完後,預先快取的內容正確的注入到precahce([])的陣列中。

接著處理完預先快取,那麼動態快取呢?

處理動態快取資源

//https://firebasestorage.googleapis.com/v0/b/days-pwas-practice.appspot.co
//https://fonts.gstatic.com/s/materialicons/v34/2fcrYFNaTjcS6g4U3t-Y5ZjZjT5FdEJ140U2DJYC3mY.woff2
workboxSW.router.registerRoute(/.*(?:googleapis|gstatic)\.com.*$/, workboxSW.strategies.staleWhileRevalidate({
    cacheName: 'fonts-and-firebase-images'
}));

在該專案中,我們引外用來資源的有兩個url
藉由workboxSW.router.registerRoute('url規則',要做的事情)可以去比對網址,當符合googleapis.comgstatic.com兩個網址時,就進入後面的workboxSW.strategies.staleWhileRevalidate(),如同fetch到上述兩種網址時,就將資源放入fonts-and-firebase-images中。

npm run generate-sw

替換一下原本使用的Service worker,重整網頁後,會發現跟原本fetch資源後,的動態快取效果一樣。
https://ithelp.ithome.com.tw/upload/images/20180114/20103808s61e71IYap.png

處理POST 文章資料

fetch監聽事件中,我們撰寫過,底下將文章寫入資料庫的程式。

var url = 'https://days-pwas-practice.firebaseio.com/article.json';
    if(-1 < event.request.url.indexOf(url)){
        event.respondWith(     
            fetch(event.request)
                .then(function(response){
                    var copyRes = response.clone();
                    clearAllData('article')
                        .then(function(){
                            return copyRes.json();
                        })
                        .then(function(data){
                            console.log('copyRes.json()',data);
                            for(var key in data){
                                console.log('key',key);
                                writeData('article',data[key]);
                            }
                        });
                    return response;
                })
        );
    }

我們可以很簡單的移植這段程式,到workbox的程式碼中,如下

workboxSW.router.registerRoute('https://days-pwas-practice.firebaseio.com/article.json', function(args){
    return fetch(args.event.request)
            .then(function(response){
                var copyRes = response.clone();
                clearAllData('article')
                    .then(function(){
                        return copyRes.json();
                    })
                    .then(function(data){
                        console.log('copyRes.json()',data);
                        for(var key in data){
                            console.log('key',key);
                            writeData('article',data[key]);
                        }
                    });
                return response;
            })
    }
);

前面的參數放入url,當fetch到該網址時,則進入此功能(handleFetch),
裡面的功能根原本一樣,一樣能透過event.request捕抓到資源,並達到原本要做的事情。

學習資源

[https://developers.google.com/web/tools/workbox/reference-docs/latest/module-workbox-sw.Router](Workbox Class)
https://developers.google.com/web/tools/workbox/


上一篇
Day28-Push Notification伺服器推播訊息實作
下一篇
Day30-Angular5實作PWA及完賽心得
系列文
30天走訪Progressive Web Apps(PWAs)30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言