使用服務工作者來管理網路要求

服務工作者是特殊類型的 Web 背景工作 角色,能夠使用 Fetch API 攔截、修改和回應網路要求。 服務工作者可以存取 Cache API 和非同步用戶端資料存放區,例如 IndexedDB ,以儲存資源。

服務工作者可以藉由在本機快取資源來加快您的 PWA 速度,也可以讓您的 PWA 與網路無關,使其更可靠。

使用者第一次存取您的 PWA 時,會安裝其服務背景工作角色。 然後,服務背景工作角色會與您的應用程式平行執行,即使您的應用程式未執行,也可以繼續執行工作。

服務工作者負責攔截、修改和回應網路要求。 當應用程式嘗試從伺服器載入資源,或傳送要求從伺服器取得資料時,可以發出警示。 發生這種情況時,服務工作者可以決定讓要求移至伺服器,或攔截它,並改為從快取傳迴響應。

高階架構圖,顯示服務背景工作角色位於應用程式與網路和快取儲存體之間

註冊服務背景工作角色

與其他 Web 背景工作角色類似,服務工作者必須存在於不同的檔案中。 您會在註冊服務背景工作角色時參考此檔案,如下列程式碼所示:

if ("serviceWorker" in navigator) {
    navigator.serviceWorker.register("/serviceworker.js");
}

執行 PWA 的網頁瀏覽器可能會為服務工作者提供不同層級的支援。 此外,PWA 執行所在的內容可能不安全。 因此,最好先測試物件是否存在, navigator.serviceWorker 再執行任何與服務背景工作角色相關的程式碼。 在上述程式碼中,服務背景工作角色是使用 serviceworker.js 位於月臺根目錄的 檔案來註冊。

請務必將服務背景工作檔案放在您想要管理的最高層級目錄中。 這類目錄稱為服務背景工作 角色的範圍 。 在先前的程式碼中,檔案會儲存在應用程式的根目錄中,而服務背景工作角色會管理應用程式功能變數名稱下的所有頁面。

如果 Service Worker 檔案儲存在目錄中 js ,則服務背景工作角色的範圍會限制為 js 目錄和任何子目錄。 最佳做法是將 Service Worker 檔案放在應用程式的根目錄中,除非您需要減少服務背景工作角色的範圍。

攔截要求

您在服務背景工作角色中使用的主要事件是 fetch 事件。 fetch每次應用程式執行的瀏覽器嘗試存取服務背景工作角色範圍內的內容時,就會執行此事件。

下列程式碼示範如何新增 事件的 fetch 接聽程式:

self.addEventListener("fetch", event => {
  console.log('WORKER: Fetching', event.request);
});

fetch 處理常式中,您可以控制要求是否進入網路、從快取提取等等。 您採取的方法可能會因所要求的資源類型、更新頻率,以及應用程式特有的其他商務邏輯而有所不同。

以下是您可以在處理常式中執行之動作的 fetch 一些範例:

  • 如果有的話,請從快取傳迴響應;否則,會後援至透過網路要求資源。
  • 從網路擷取資源、快取複本,然後傳迴響應。
  • 允許使用者指定儲存資料的喜好設定。
  • 提供特定映射要求的預留位置映射。
  • 直接在服務背景工作角色中產生回應。

服務工作者生命週期

服務工作者的生命週期包含多個步驟,每個步驟都會觸發事件。 您可以將接聽程式新增至這些事件,以執行程式碼來執行動作。 下列清單提供服務工作者生命週期和相關事件的高階檢視。

  1. 註冊服務背景工作角色。

  2. 瀏覽器會下載 JavaScript 檔案、安裝服務背景工作角色,並觸發 install 事件。 您可以使用 install 事件預先快取任何重要且長期存在的檔案, (例如 CSS 檔案、JavaScript 檔案、標誌影像,或從應用程式) 離線頁面。

    self.addEventListener("install", event => {
        console.log("WORKER: install event in progress.");
    });
    
  3. 服務背景工作角色會啟動,這會 activate 觸發事件。 使用此事件來清除過時的快取。

    self.addEventListener("activate", event => {
        console.log("WORKER: activate event in progress.");
    });
    
  4. 當頁面重新整理或使用者移至網站上的新頁面時,服務工作者已準備好執行。 如果您想要在不等候的情況下執行服務背景工作角色,請在事件期間 install 使用 self.skipWaiting() 方法,如下所示:

    self.addEventListener("install", event => {
        self.skipWaiting();
        // …
    });
    
  5. 服務工作者現在正在執行,而且可以接 fetch 聽事件。

預先快取資源

當使用者第一次存取您的應用程式時,會安裝應用程式的服務背景工作角色。 install在您的服務背景工作角色中使用 事件來偵測何時發生這種情況,並快取應用程式所需的所有靜態資源。 快取應用程式的靜態資源 (例如起始頁面所需的 HTML、CSS 和 JavaScript 程式碼) ,讓您的應用程式能夠在使用者裝置離線時執行。

若要快取應用程式的資源,請使用全 caches 局物件和 cache.addAll 方法,如下所示:

// The name of the cache your app uses.
const CACHE_NAME = "my-app-cache";
// The list of static files your app needs to start.
const PRE_CACHED_RESOURCES = ["/", "styles.css", "app.js"];

// Listen to the `install` event.
self.addEventListener("install", event => {
  async function preCacheResources() {
    // Open the app's cache.
    const cache = await caches.open(CACHE_NAME);
    // Cache all static resources.
    cache.addAll(PRE_CACHED_RESOURCES);
  }

  event.waitUntil(preCacheResources());
});

請注意,在初始安裝之後, install 事件不會再次執行。 若要更新服務背景工作角色的程式碼,請參閱 更新您的服務背景工作角色

現在您可以使用 fetch 事件從快取傳回靜態資源,而不是再次從網路載入它們:

self.addEventListener("fetch", event => {
  async function returnCachedResource() {
    // Open the app's cache.
    const cache = await caches.open(CACHE_NAME);
    // Find the response that was pre-cached during the `install` event.
    const cachedResponse = await cache.match(event.request.url);

    if (cachedResponse) {
      // Return the resource.
      return cachedResponse;
    } else {
      // The resource wasn't found in the cache, so fetch it from the network.
      const fetchResponse = await fetch(event.request.url);
      // Put the response in cache.
      cache.put(event.request.url, fetchResponse.clone());
      // And return the response.
      return fetchResponse.
    }
  }

  event.respondWith(returnCachedResource());
});

為了簡潔起見,上述程式碼範例不會處理從網路取得要求 URL 失敗的情況。

使用自訂離線頁面

當您的應用程式使用多個 HTML 頁面時,常見的離線案例是在使用者的裝置離線時,將頁面導覽要求重新導向至自訂錯誤頁面:

// The name of the cache your app uses.
const CACHE_NAME = "my-app-cache";
// The list of static files your app needs to start.
// Note the offline page in this list.
const PRE_CACHED_RESOURCES = ["/", "styles.css", "app.js", "/offline"];

// Listen to the `install` event.
self.addEventListener("install", event => {
  async function preCacheResources() {
    // Open the app's cache.
    const cache = await caches.open(CACHE_NAME);
    // Cache all static resources.
    cache.addAll(PRE_CACHED_RESOURCES);
  }

  event.waitUntil(preCacheResources());
});

self.addEventListener("fetch", event => {
  async function navigateOrDisplayOfflinePage() {
    try {
      // Try to load the page from the network.
      const networkResponse = await fetch(event.request);
      return networkResponse;
    } catch (error) {
      // The network call failed, the device is offline.
      const cache = await caches.open(CACHE_NAME);
      const cachedResponse = await cache.match("/offline");
      return cachedResponse;
    }
  }

  // Only call event.respondWith() if this is a navigation request
  // for an HTML page.
  if (event.request.mode === 'navigate') {
    event.respondWith(navigateOrDisplayOfflinePage());
  }
});

更新您的服務背景工作角色

安裝新的服務背景工作角色版本

如果您對 Service Worker 程式碼進行變更,並將新的 Service Worker 檔案部署到網頁伺服器,則使用者的裝置會逐漸開始使用新的服務背景工作角色。

每次使用者流覽至您應用程式的其中一個頁面時,執行應用程式的瀏覽器會檢查伺服器上是否有新版的服務背景工作角色。 瀏覽器會藉由比較現有服務背景工作角色與新服務背景工作角色之間的內容來偵測新版本。 偵測到變更時,會在事件) 觸發 (install 安裝新的服務背景工作角色,然後新的服務背景工作角色會等候現有的服務背景工作角色停止在裝置上使用。

在實務上,這表示可以有兩個服務工作者同時執行,但只有一個會攔截應用程式的網路要求。 當應用程式關閉時,會停止使用現有的服務背景工作角色。 下次開啟應用程式時,會啟動新的服務背景工作角色。 事件 activate 會觸發,而新的服務背景工作角色會開始攔截 fetch 事件。

您可以在服務背景工作角色的事件處理常式中使用 self.skipWaiting() ,在安裝新服務背景 install 工作角色後立即強制啟用它。

若要深入瞭解服務背景工作角色的更新方式,請參閱在 web.dev 上 更新服務背景 工作角色。

更新快取的靜態檔案

如預先快取資源中所述,預先快取 CSS 樣式表單檔案等靜態 資源時,您的應用程式只會使用檔案的快取版本,而且不會嘗試下載新版本。

若要確保使用者取得應用程式所使用靜態資源的最新變更,請使用快取破壞命名慣例,並更新您的服務背景工作程式碼。

快取擷取 表示每個靜態檔案都會根據其版本命名。 這可以透過各種方式來達成,但通常牽涉到使用建置工具來讀取檔案的內容,並根據內容產生唯一識別碼。 該識別碼可用來命名快取的靜態檔案。

接下來,更新您的服務背景工作程式碼,以在 期間 install 快取新的靜態資源:

// The name of the new cache your app uses.
const CACHE_NAME = "my-app-cache-v2";
// The list of static files your app needs to start.
const PRE_CACHED_RESOURCES = ["/", "styles-124656.css", "app-576391.js"];

// Listen to the `install` event.
self.addEventListener("install", event => {
  async function preCacheResources() {
    // Open the app's cache.
    const cache = await caches.open(CACHE_NAME);
    // Cache the new static resources.
    cache.addAll(PRE_CACHED_RESOURCES);
  }

  event.waitUntil(preCacheResources());
});

// Listen to the `activate` event to clear old caches.
self.addEventListener("activate", event => {
  async function deleteOldCaches() {
    // List all caches by their names.
    const names = await caches.keys();
    await Promise.all(names.map(name => {
      if (name !== CACHE_NAME) {
        // If a cache's name is the current name, delete it.
        return caches.delete(name);
      }
    }));
  }

  event.waitUntil(deleteOldCaches());
});

CACHE_NAME比較上述程式碼片段與預先快取資源中程式碼片段之間的 和 PRE_CACHED_RESOURCES 值。 安裝這個新的服務背景工作角色時,將會建立新的快取,並下載並快取新的靜態資源。 啟動服務背景工作角色時,將會刪除舊的快取。 此時,使用者將擁有新版本的應用程式。

對您的服務背景工作角色進行變更有時可能很複雜。 使用 Workbox 之類的程式庫來簡化靜態資源建置步驟和服務背景工作程式碼。

測試 PWA 中的網路連線

瞭解網路連線何時可用,以便同步處理資料或通知使用者網路狀態已變更,會很有説明。

使用下列選項來測試網路連線能力:

屬性 navigator.onLine 是布林值,可讓您知道網路的目前狀態。 如果值為 true ,則使用者在線上,否則使用者會離線。

若要深入瞭解, 請參閱 MDN 上的 navigator.onLine。

線上和離線事件

您可以在網路連線能力變更時採取動作。 您可以接聽並採取動作來回應網路事件。 事件可在 、 documentdocument.body 元素上 window 使用,如下所示:

window.addEventListener("online",  function(){
    console.log("You are online!");
});
window.addEventListener("offline", function(){
    console.log("Oh no, you lost your network connection.");
});

若要深入瞭解, 請參閱 MDN 上的 Navigator.onLine。

其他功能

服務工作者的主要責任是在網路連線不穩定時,讓您的應用程式更快速且更可靠。 服務工作者大多會使用 fetch 事件和 Cache API 來執行這項操作,但他們可以針對進階案例使用其他 API,例如:

  • 資料的背景同步處理。
  • 資料的定期同步處理。
  • 大型背景檔案下載。
  • 處理和通知推播訊息。

背景同步處理

使用背景同步 API 可讓使用者繼續使用您的應用程式,並執行動作,即使使用者的裝置離線也一樣。

例如,電子郵件應用程式可以讓其使用者隨時撰寫和傳送訊息。 應用程式前端可以立即嘗試傳送訊息,如果裝置離線,服務工作者可以攔截失敗的要求,並使用背景同步 API 來延遲工作,直到連線為止。

若要深入瞭解,請 參閱使用背景同步 API 與伺服器同步處理資料

期間背景同步處理

定期背景同步 API 可讓 PWA 在背景定期擷取全新的內容,讓使用者稍後再次開啟應用程式時,可以立即存取內容。

藉由使用定期背景同步 API,PWA 不需要下載新的內容 (例如使用者使用應用程式時) 的新文章。 下載內容可能會讓體驗變慢,因此應用程式可以在更方便的時間擷取內容。

若要深入瞭解,請 參閱使用定期背景同步 API 定期取得全新的內容

大型背景檔案下載

背景擷取 API 可讓 PWA 將下載大量資料完全委派給瀏覽器引擎。 如此一來,應用程式和服務背景工作角色就不需要在下載進行時執行。

此 API 適用于可讓使用者針對離線使用案例下載大型檔案 (例如音樂、電影或播客) 的應用程式。 下載會委派給瀏覽器引擎,而瀏覽器引擎知道如何處理間歇性連線,甚至是完全中斷連線。

若要深入瞭解,請 參閱當應用程式或服務背景工作角色未執行時,使用背景擷取 API 來擷取大型檔案

推送訊息

推送訊息可以傳送給您的使用者,而不必在當時使用應用程式。 即使應用程式未執行,服務工作者也可以接聽伺服器所傳送的推播訊息,並在作業系統的通知中心顯示通知。

若要深入瞭解,請 參閱使用推播訊息重新吸引使用者

使用 DevTools 進行偵錯

使用 Microsoft Edge DevTools,您可以查看您的服務背景工作角色是否已正確註冊,並查看 Service Worker 目前處於哪個生命週期狀態。 此外,您可以在服務背景工作角色中偵錯 JavaScript 程式碼。

若要深入瞭解,請參閱 偵錯您的服務背景工作角色

另請參閱