Service Worker' 的生活

如果不瞭解服務工作處理程序的生命週期,將很難知道自己正在做什麼。 他們的內部工作看起來是不透明的,甚至沒有。 請記住,就像任何其他瀏覽器 API 一樣,Service Worker 的行為都是由明確定義。 以及提供離線應用程式 同時讓使用者在不影響使用者體驗的情況下進行更新。

開始使用 Workbox 前 請務必瞭解 Service Worker 生命週期,這樣 Workbox 有何意義。

定義字詞

進入 Service Worker 生命週期之前 建議您針對該生命週期的運作方式定義一些詞彙

控管與範圍

「控制」的概念對於瞭解服務工作處理程序的方式至關重要。 由 Service Worker「控制」的頁面是指可讓服務工作處理程序代表網路要求攔截網路要求的網頁。 Service Worker 會存在「而且」可以在指定範圍內為網頁執行作業。

範圍

Service Worker 的「範圍」取決於其在網路伺服器上的位置。 如果 Service Worker 在位於 /subdir/index.html 的網頁上執行,且位於 /subdir/sw.js, Service Worker 的範圍為 /subdir/。 如要瞭解範圍概念的實際運作方式,請查看以下範例:

  1. 前往 https://service-worker-scope-viewer.glitch.me/subdir/index.html. 系統會顯示訊息,指出目前沒有 Service Worker 控制該頁面。 不過,該網頁會從 https://service-worker-scope-viewer.glitch.me/subdir/sw.js 註冊 Service Worker。
  2. 重新載入網頁。Service Worker 已註冊並現已啟用, 控制網頁 一個表單,內含 Service Worker 的範圍。 和其網址 注意:必須重新載入頁面與範圍無關 而是 Service Worker 的生命週期,後續會再說明
  3. 現在請前往 https://service-worker-scope-viewer.glitch.me/index.html。 雖然 Service Worker 已註冊在這個來源中 中仍會顯示目前沒有 Service Worker 的訊息。 因為這個頁面不在已註冊的 Service Worker 範圍內。

範圍會限制服務工作處理程序控制的頁面。 在此範例中,這表示從 /subdir/sw.js 載入的 Service Worker 只能控制位於 /subdir/ 或其子樹狀結構中的頁面。

以上是範圍預設的運作方式 但可以透過設定 Service-Worker-Allowed 回應標頭, 以及將 設為 register 方法的 scope 選項。

除非有充分理由將 Service Worker 範圍限制在來源的子集,否則 會從網路伺服器的根目錄載入 Service Worker,盡可能擴大範圍 請不用擔心 Service-Worker-Allowed 標頭這麼簡單,每個人都能輕鬆上手。

用戶端

當服務工作人員說網頁正在控制網頁,它其實是在控制用戶端。 用戶端是指任何開啟的網頁,其網址位於 Service Worker 的範圍內。 具體來說,這些是 WindowClient 的執行個體。

新 Service Worker 的生命週期

為了讓服務工作處理程序控制網頁, 那就必須先進入存在,那就這樣吧 讓我們先從為沒有有效服務工作人員的網站部署全新 Service Worker 時,會發生什麼情況。

註冊

註冊是 Service Worker 生命週期的初始步驟:

<!-- In index.html, for example: -->
<script>
  // Don't register the service worker
  // until the page has fully loaded
  window.addEventListener('load', () => {
    // Is service worker available?
    if ('serviceWorker' in navigator) {
      navigator.serviceWorker.register('/sw.js').then(() => {
        console.log('Service worker registered!');
      }).catch((error) => {
        console.warn('Error registering service worker:');
        console.warn(error);
      });
    }
  });
</script>

這段程式碼會在主執行緒上執行,並執行以下操作:

  1. 因為使用者首次造訪網站時,沒有註冊的服務工作人員, 等網頁完全載入後再註冊 如果 Service Worker 會預先快取任何項目,這樣可以避免頻寬爭用。
  2. 雖然 Service Worker 受到廣泛支援, 快速檢查有助於避免在不支援 Chrome 瀏覽器的瀏覽器中發生錯誤。
  3. 網頁完全載入後,如果支援 Service Worker,請註冊 /sw.js

以下是一些需要注意的重點:

  • Service Worker 只能透過 HTTPS 或 localhost 取得
  • 如果 Service Worker 的內容包含語法錯誤, 註冊失敗,並捨棄 Service Worker。
  • 提醒:Service Worker 會在範圍內運作。 這裡,範圍是整個來源,因為它是從根目錄載入。
  • 登錄開始時,Service Worker 狀態會設為 'installing'

註冊完成後,安裝作業隨即開始。

安裝

Service Worker 觸發了 install 事件。 每個 Service Worker 只會呼叫一次 install,在更新之前不會再次觸發。 install 事件的回呼可使用 addEventListener 在 worker 範圍內註冊:

// /sw.js
self.addEventListener('install', (event) => {
  const cacheKey = 'MyFancyCacheName_v1';

  event.waitUntil(caches.open(cacheKey).then((cache) => {
    // Add all the assets in the array to the 'MyFancyCacheName_v1'
    // `Cache` instance for later use.
    return cache.addAll([
      '/css/global.bc7b80b7.css',
      '/css/home.fe5d0b23.css',
      '/js/home.d3cc4ba4.js',
      '/js/jquery.43ca4933.js'
    ]);
  }));
});

這項操作會建立新的 Cache 執行個體並預先快取資產。 我們稍後將有許多機會可以討論預先快取 所以我們先來介紹 event.waitUntilevent.waitUntil接受承諾, 並等待模型解決 在這個例子中,保證會執行以下兩種非同步的作業:

  1. 建立名為 'MyFancyCache_v1' 的新 Cache 執行個體。
  2. 快取建立完成後 會以非同步方式預先載入資產網址陣列 addAll 方法

如果傳遞至 event.waitUntil 的承諾如下,安裝就會失敗 已拒絕。 如果發生這種情況,系統會捨棄 Service Worker。

如果保證解決, 安裝成功,Service Worker 的狀態會變更為 'installed' 然後啟動。

啟用

如果註冊和安裝成功 Service Worker 會啟動,且狀態變為 'activating' 作業可在 Service Worker 的 activate 事件。 此事件中的典型工作是修剪舊快取, 但對於全新的服務人員 目前也不太相關 規模擴大到我們談到服務工作人員的最新消息

如果是新的 Service Worker,activate 會在 install 成功後立即觸發。 啟用完成後 Service Worker 的狀態會變成 'activated'。 請注意,根據預設 新的 Service Worker 要等到下一次瀏覽或重新整理頁面後,才能開始控制頁面。

處理 Service Worker 更新

部署第一個 Service Worker 後 這可能在稍後更新 例如,如果要求處理或預先快取邏輯有所變更,就可能必須進行更新。

有更新時

在下列情況下,瀏覽器會檢查 Service Worker 的更新狀態:

更新方式

瞭解瀏覽器更新 Service Worker 的「時間」很重要, 而是「如何」假設 Service Worker 的網址或範圍並未變更, 目前安裝的 Service Worker 只會在新版本內容有所變更時,才更新至新版本。

瀏覽器偵測變更的方式有幾種:

  • importScripts (如適用)。
  • Service Worker 頂層程式碼中的任何變更, 這會影響瀏覽器產生的指紋

瀏覽器在這裡處理大量工作。 為了確保瀏覽器具備所有服務,可穩定偵測 Service Worker 內容的變更, 不要指示 HTTP 快取保留快取,也不會變更其檔案名稱。 瀏覽器在 Service Worker 範圍內瀏覽新網頁時,瀏覽器會自動執行更新檢查。

手動觸發更新檢查

請留意,註冊邏輯通常不應改變。 不過,如果網站工作階段效期較長,就可能屬於例外情況。 單一頁面應用程式中可能會發生這種情況 瀏覽要求的情況很少見 因為應用程式在應用程式的生命週期開始時,通常會遇到一個導覽要求。 在這種情況下,可以在主執行緒上觸發手動更新:

navigator.serviceWorker.ready.then((registration) => {
  registration.update();
});

如果是傳統網站 或者在任何使用者工作階段未長期使用的情況下 可能並不需要觸發手動更新。

安裝

使用整合工具產生靜態資產時 這些素材資源的名稱會包含雜湊 例如 framework.3defa9d2.js。 假設其中部分素材資源為預載,方便稍後離線存取。 這麼做需要更新 Service Worker 來預先快取更新後的資產:

self.addEventListener('install', (event) => {
  const cacheKey = 'MyFancyCacheName_v2';

  event.waitUntil(caches.open(cacheKey).then((cache) => {
    // Add all the assets in the array to the 'MyFancyCacheName_v2'
    // `Cache` instance for later use.
    return cache.addAll([
      '/css/global.ced4aef2.css',
      '/css/home.cbe409ad.css',
      '/js/home.109defa4.js',
      '/js/jquery.38caf32d.js'
    ]);
  }));
});

以下兩個情況與前面的第一個 install 事件範例不同:

  1. 已建立新的 Cache 執行個體,金鑰為 'MyFancyCacheName_v2'
  2. 友善快取的資產名稱已變更。
,瞭解如何調查及移除這項存取權。

值得一提的是,更新過的 Service Worker 會與先前的版本一起安裝。 這表示舊的 Service Worker 仍可掌控任何開啟的網頁,並在安裝作業後 新的觸發條件就會進入等待狀態,直到狀態啟用為止。

根據預設,當舊用戶端沒有控管任何用戶端時,新的 Service Worker 就會啟用。 如果相關網站所有開啟的分頁都關閉,就會發生這種狀況。

啟用

安裝更新過的 Service Worker 並等待階段結束時, 啟動後,系統會捨棄舊的 Service Worker。 在更新後的 Service Worker 的 activate 事件中執行的常見工作是修剪舊快取。 針對所有已開啟的 Cache 執行個體,取得其金鑰以移除舊快取 caches.keys 、刪除不在定義允許清單中的快取,以及 caches.delete

self.addEventListener('activate', (event) => {
  // Specify allowed cache keys
  const cacheAllowList = ['MyFancyCacheName_v2'];

  // Get all the currently active `Cache` instances.
  event.waitUntil(caches.keys().then((keys) => {
    // Delete all caches that aren't in the allow list:
    return Promise.all(keys.map((key) => {
      if (!cacheAllowList.includes(key)) {
        return caches.delete(key);
      }
    }));
  }));
});

舊的快取不會自行清理。 我們必須自己動手, 儲存空間配額。 由於第一個 Service Worker 的 'MyFancyCacheName_v1' 版本過舊, 更新快取許可清單以指定 'MyFancyCacheName_v2' 就會刪除不同名稱的快取。

移除舊的快取後,activate 事件就會結束。 此時,新的 Service Worker 會控管頁面。 終於快換個新月了!

生命週期永遠是

無論是否使用 Workbox 處理 Service Worker 的部署與更新, 或者直接使用 Service Worker API 瞭解 Service Worker 的生命週期 瞭解這一點後,服務工作人員的行為看起來應該比神祕學更符合邏輯。

如果有興趣深入瞭解這個主題 不妨試試 這篇文章。 服務生命週期周圍的舞步有極為細微差異 但這份知識相當值得瞭解,在使用 Workbox 時,知識將大不相同。