PWA之 workbox 學習

前言:
我們的應用可以分爲兩部分,一部分是屬於主進程的(包括js(同步,異步),以及dom渲染等等),在一個時刻點,只能執行一個,要麼先去渲染dom,完了再去執行js;要麼執行完js,在去渲染dom,而不能同時執行js和dom渲染。 另一部分屬於worker進程,它重新在後臺起了一個進程,它和應用的主進程互不影響,可以同時執行。

常見的worker有,web worker, service worker, shared worker等等。

其中service worker一般作爲web應用程序、瀏覽器和網絡(如果可用)之間的代理服務。他們旨在創建有效的離線體驗,攔截網絡請求,以及根據網絡是否可用採取合適的行動,更新駐留在服務器上的資源。他們還將允許訪問推送通知和後臺同步API。用來構建PWA 應用

使用service-worker前,我們必須要先在主進程中註冊它,然後才能在service-worker進程中編寫邏輯。

主進程

//index.js
if ("serviceWorker" in navigator) {
    // Use the window load event to keep the page load performant
    window.addEventListener("load", () => {
      navigator.serviceWorker.register("/service-worker.js").then(registration=>{
        console.log("register succces...")
      }, err=>{
        console.log("register error...",err)
      });
    });
  }

service-worker進程

//service-worker.js
console.log('Hello from service-worker.js');
service-worker的語法簡介

在service-worker.js中,self/this 表示 ServiceWorkerGlobalScope, 即全局的serviceworker工作環境,相當於在主進程中的window。在此文件中,js的其他api無法使用,如DOM,BOM操作等,但是大部分的js api可用,同時ES6也可以使用。

在service-worker中可以定義監聽事件,然後在對應事件中進行邏輯處理。

具體api可查看 service worker MDN

service-worker進程的執行流程

  1. 首先在主進程中開始註冊,調用register方法,進入sw進程,在sw進程中判斷如果還沒有安裝service worker.js,則觸發install事件。開始安裝
  2. 一旦sw進程安裝完成,會通知主進程register成功。
  3. 接着在sw進程 觸發到activate事件。
  4. 如果已經安裝過service-worker.js文件,則在註冊時會發現並跳過install事件,直接進入註冊成功,然後觸發activate事件。
  5. 然後開始在sw進程中通過fetch事件,來監聽http請求,並對請求和響應進行緩存。
//在service worker中監聽install
this.addEventListener('install', function(event) {
  event.waitUntil(
    caches.open('v1').then(function(cache) {
      return cache.addAll([
        '/sw-test/',
        '/sw-test/index.html',
        '/sw-test/style.css',
        '/sw-test/app.js',
        '/sw-test/image-list.js',
        '/sw-test/star-wars-logo.jpg',
        '/sw-test/gallery/',
        '/sw-test/gallery/bountyHunters.jpg',
        '/sw-test/gallery/myLittleVader.jpg',
        '/sw-test/gallery/snowTroopers.jpg'
      ]);
    })
  );
});

除了 install之外,還有 activate,message,fetch,sync,push等事件。

打開chrome瀏覽器的application->service workers,會看到
image
可以看到status爲 actived and is running,表明service-worker已經安裝成功了。

在service-worker中通過監聽事件,然後編寫對應的邏輯並不是一件容易的事,尤其對於文件緩存,可能npm run build後,名稱隨時會變。

所以chrome官方推出了wokbox框架

clipboard.png

wokbox 是用於向web應用程序添加離線支持的JavaScript庫。

要使用wokbox,只需在service-worker.js文件中引入workbox-sw.js即可,然後會自動的在service-worker.js中創建workbox對象,

importScripts('https://storage.googleapis.com/workbox-cdn/releases/4.3.1/workbox-sw.js');

if (workbox) {
  console.log(`Yay! Workbox is loaded 🎉`);
} else {
  console.log(`Boo! Workbox didn't load 😬`);
}

在wokbox對象中,包含很多模塊,比如 workbox.routing模塊,workbox.precaching模塊,workbox.strategies模塊,workbox.expiration模塊等等,它們分別負責處理不同的邏輯。

1、workbox緩存/預緩存

//緩存文件
workbox.routing.registerRoute(
  /\.css$/,   //通過正則匹配需要緩存哪些文件
  new workbox.strategies.StaleWhileRevalidate({
    cacheName: 'css-cache',  //緩存名,可在application-> Cache storage下找到
  })
);

workbox.routing.registerRoute(
  /\.(?:js)$/,
  new workbox.strategies.CacheFirst({
    cacheName: 'js-cache',
    plugins: [
      //設置過期時間和最大數量
      new workbox.expiration.Plugin({
        maxEntries: 20,
        maxAgeSeconds: 7 * 24 * 60 * 60,
      })
    ],
  })
);

workbox.routing.registerRoute表明 當service-worker在安裝之後,當頁面有發送對應http請求時,開始緩存。

而下面的workbox.precaching.precacheAndRoute可以在service-worker在安裝之前,就把對應文件預先緩存下來。

workbox.precaching.precacheAndRoute([
  "/app.0.css",
  "/app.bundle.js",
  { url: "/start.html", revision: "dd75b1ef1ac2d4726b03fe46e90423f1" }
]);

此時我們在chrome下的 application->cache storage,會看到
image

緩存的名稱,和緩存的文件列表

2、workbox路由

首先了解下處理路由的workbox的策略

  • StaleWhileRevalidate, 此策略將對請求使用緩存響應,並在後臺使用網絡響應更新緩存。如果沒有緩存,它將等待網絡響應並使用它),這是一種相當安全的策略,因爲這意味着用戶會定期更新其緩存。
  • NetworkFirst,這將首先嚐試從網絡獲取請求。如果收到響應,它會將其傳遞給瀏覽器並將其保存到緩存中。如果網絡請求失敗,將使用最後一個緩存的響應。
  • CacheFirst,此策略將首先檢查緩存中的響應,如果有可用則使用該策略。如果請求不在緩存中,則將使用網絡,並且在傳遞給瀏覽器之前,任何有效響應都將添加到緩存中。
  • NetworkOnly,強制從網絡獲取。
  • CacheOnly,,強制從緩存獲取。
workbox.routing.registerRoute(
  '/logo.png',    //匹配字符串路由
  new workbox.strategies.NetworkFirst()    //採用NetworkFirst策略
);
workbox.routing.registerRoute(
  /\.js$/,   // 配置 正則 路由,
  new workbox.strategies.StaleWhileRevalidate(),  //採用StaleWhileRevalidate策略
); 
//緩存第三方,比如jquery, 則策略最好使用NetworkFirst或者StaleWhileRevalidate, 不要使用CacheFirst
workbox.routing.registerRoute(
  'https://cdn.bootcss.com/jquery/3.4.1/jquery.js',
  new workbox.strategies.StaleWhileRevalidate(),
  //new workbox.strategies.NetworkFirst(),
); 

//如果非要使用CacheFirst策略,則使用workbox.cacheableResponse.Plugin限定
workbox.routing.registerRoute(
  'https://cdn.bootcss.com/jquery/3.4.1/jquery.js',
  new workbox.strategies.CacheFirst({
    plugins: [
      new workbox.cacheableResponse.Plugin({
        statuses: [0, 200]
      })
    ]
  }),
);
//還可以自定義策略的名稱,過期時間等等
workbox.routing.registerRoute(
  /\.(?:js)$/,
  new workbox.strategies.CacheFirst({
    cacheName: 'js-cache',
    plugins: [
      new workbox.expiration.Plugin({
        maxEntries: 20,
        maxAgeSeconds: 7 * 24 * 60 * 60,
      })
    ],
  })
);

在前面我們看到 cache的名稱爲 workbox-precache-v2-http://127.0.0.1:8081/,下面我們修改下

//最好寫在緊貼着importScripts workbox-sw.js的下面,如果寫在文件最後,則不生效。
workbox.core.setCacheNameDetails({
  prefix: "my-app",
  suffix: "v1",
  precache: "custom-precache-name",
  runtime: "custom-runtime-name"
});

image

3、workbox插件

  • workbox.backgroundSync.Plugin:如果網絡請求失敗,請將其添加到後臺同步隊列,並在觸發下一個同步事件時重試該請求。
  • workbox.broadcastUpdate.Plugin:每當緩存更新調度時,廣播頻道上的消息或通過 postMessage()。
  • workbox.cacheableResponse.Plugin:僅緩存符合特定條件的緩存請求。
  • workbox.expiration.Plugin:管理緩存中項目的數量和最長期限。
  • workbox.rangeRequests.Plugin:響應包含Range:標頭的請求,其中包含來自緩存的部分內容。

4、workbox debug

importScripts("https://storage.googleapis.com/workbox-cdn/releases/4.3.1/workbox-sw.js");
importScripts("precache-manifest.7df9e91fe595ae52486747ebe221a710.js");

//強制在service worker中使用debug。這樣service worker中的log也能打印到chrome console上
workbox.setConfig({
  debug: true
});

image

4、在webpack中 集成 workbox,來自動的生成service worker

yarn add workbox-webpack-plugin
//webpack.config.js:
const WorkboxPlugin = require('workbox-webpack-plugin');

module.exports = {
  // Other webpack config...

  plugins: [
    // Other plugins...
    
    new WorkboxPlugin.GenerateSW()
  ]
};

然後執行

npm run build

此時在dist目錄下會自動生成precache-manifest.<revision>.js 和 service-worker.js文件(爲什麼名字是這個,不是sw.js,因爲在註冊時register("/service-worker.js")寫的這個名字),如圖:

image

在precache-manifest.<revision>.js文件中,將預緩存列表通過全局變量self.__precacheManifest公開,以便在service-worker.js中調用。
默認會預緩存一切資源。
image

在service-worker.js中,則自動加載workbox cdn和 precache-manifest.<revision>.js文件,如圖:
image

瞬間感覺方便了很多........

因爲默認會預緩存一切資源,如果你不喜歡預緩存某些文件,如圖片,而在運行時緩存,則可以在runtimeCaching中定製它們

// 這些選項幫助 ServiceWorkers 快速啓用
new WorkboxPlugin.GenerateSW({
      // 在預緩存中排除 圖片
      exclude: [/\.(?:png|jpg|jpeg|svg)$/],

      //定義運行時緩存(可接受多個json對象)
      runtimeCaching: [
        {
          urlPattern: /\.(?:png|jpg|jpeg|svg)$/,
          // 在緩存時使用 StaleWhileRevalidate 策略.
          handler: "StaleWhileRevalidate",
          options: {
            // 定義緩存這些圖片的 cache名稱
            cacheName: "my-images-cache",

            //配置 expiration
            expiration: {
              maxEntries: 10,
              maxAgeSeconds: 60
            },

            // 配置 background sync.
            backgroundSync: {
              name: "my-queue-name",
              options: {
                maxRetentionTime: 60 * 60
              }
            },

            //配置哪些響應被認爲是可緩存的
            cacheableResponse: {
              statuses: [0, 200],
              headers: { "x-test": "true" }
            },

            //配置廣播緩存更新插件。
            broadcastUpdate: {
              channelName: "my-update-channel"
            },

            //matchOptions和fetchOptions用於配置handler
            fetchOptions: {
              mode: "no-cors"
            },
            matchOptions: {
              ignoreSearch: true
            }
          }
        }
      ],

      importWorkboxFrom: "cdn", //通過cdn加載workbox, 還可通過‘local’加載,這樣會將整個workbox下載到本地,再從本地引用
      skipWaiting: false, // service worker是否應該跳過等待生命週期階段
      clientsClaim: false, //service worker是否應該在任何現有客戶端激活後立即開始控制它

      cacheId: "my-app-test",
      offlineGoogleAnalytics: true
    })

運行npm run build後,會看到自動生成如下的service-worker.js
image

如果沒出現,則只需要清空cache和service-worker文件即可,在clear storage中勾選unregister service workers和 cache storage,然後點擊clear site data即可清理,然後刷新頁面就會看到最新的service worker和cache storage。
image

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章