從 Workbox v4 遷移至 v5

本指南著重於 Workbox 第 5 版中的破壞性變更,並說明您在從 Workbox v4 升級時需要進行哪些變更。

破壞性變更

已重新命名外掛程式類別

許多 Workbox v4 套件包含名為 Plugin 的類別。在 v5 中,這些類別已重新命名,以遵循模式套件 ID + Plugin

  • BackgroundSyncPlugin
  • BroadcastUpdatePlugin
  • CacheableResponsePlugin
  • ExpirationPlugin
  • RangeRequestsPlugin

無論您是透過模組匯入功能使用類別,還是透過 workbox.* 命名空間使用類別,都適用這個重新命名方式。

預設預先快取資訊清單替換點

先前,在「插入資訊清單」模式下使用其中一個建構工具時,系統會檢查來源 Service Worker 檔案是否存在 precacheAndRoute([]),並以該空白陣列 [] 做為插入預載資訊清單插入位置的預留位置。

在 Workbox v5 中,取代邏輯已變更,現在預設會使用 self.__WB_MANIFEST 做為插入點。

// v4:
precacheAndRoute([]);

// v5:
precacheAndRoute(self.__WB_MANIFEST);

這個討論所述,我們相信這項變更提供更簡便的使用體驗,同時可讓開發人員進一步控管插入的資訊清單在自訂 Service Worker 程式碼中的使用方式。如有需要,您可以透過 injectionPoint 設定選項變更這個取代字串。

先前支援導覽路徑的兩個選項 blacklistwhitelist 已重新命名為 denylistallowlist

workbox-routing 先前支援方法 registerNavigationRoute(),實際上,它執行了以下兩項操作:

  1. 偵測到特定 fetch 事件是否有 mode'navigate'
  2. 如果是這種情況,系統會使用先前快取的硬式編碼網址內容回應該要求,不論系統前往哪個網址。

這是實作 App Shell 架構時的常見模式。

從快取讀取資料而產生回應的第二步驟,不屬於 workbox-routing 的責任。相反地,我們看來這是功能應該由 workbox-precaching 提供的新方法 createHandlerBoundToURL() 提供的功能。這個新方法可與 workbox-routing 中現有的 NavigationRoute 類別同時運作,以完成相同的邏輯。

如果您在其中一個建構工具的「產生 SW」模式中使用 navigateFallback 選項,系統會自動進行切換。如果您先前已設定 navigateFallbackBlacklistnavigateFallbackWhitelist 選項,請分別變更為 navigateFallbackDenylistnavigateFallbackAllowlist

如果您採用「插入資訊清單」模式,或只自行編寫 Service Worker,且 Workbox v4 服務工作站直接呼叫 registerNavigationRoute(),則您必須修改程式碼,才能取得對等的行為。

// v4:
import {getCacheKeyForURL} from 'workbox-precaching';
import {registerNavigationRoute} from 'workbox-routing';

const appShellCacheKey = getCacheKeyForURL('/app-shell.html');
registerNavigationRoute(appShellCacheKey, {
  whitelist: [...],
  blacklist: [...],
});

// v5:
import {createHandlerBoundToURL} from 'workbox-precaching';
import {NavigationRoute, registerRoute} from 'workbox-routing';

const handler = createHandlerBoundToURL('/app-shell.html');
const navigationRoute = new NavigationRoute(handler, {
  allowlist: [...],
  denylist: [...],
});
registerRoute(navigationRoute);

你不必再呼叫 getCacheKeyForURL()createHandlerBoundToURL() 會為你處理。

從 Workbox 策略中移除 makeRequest()

呼叫 makeRequest() 大致相當於在其中一個 workbox-strategy 類別上呼叫 handle()。這兩個方法之間的差異十分輕微,而保留兩者之間沒有意義。呼叫 makeRequest() 的開發人員應可切換為使用 handle(),且無須進行任何變更:

// v4:
const strategy = new StaleWhileRevalidate({...});
const response = await strategy.makeRequest({event, request});

// v5:
const strategy = new StaleWhileRevalidate({...});
const response = await strategy.handle({event, request});

在 v5 中,handle() 會將 request 視為必要參數,且不會改回使用 event.request。呼叫 handle() 時,請務必傳入有效的要求。

Workbox-broadcast-update 一律使用 postMessage()

在 v4 中,workbox-broadcast-update 程式庫在受到支援時會使用 Broadcast Channel API 傳送訊息,並且只有在不支援廣播頻道時才會改回使用 postMessage()

我們發現必須監聽兩個可能收到的訊息來源,導致用戶端程式碼編寫過於複雜。此外,在某些瀏覽器中,Service Worker 傳送至用戶端頁面的 postMessage() 呼叫會自動緩衝,直到設定 message 事件監聽器為止。Broadcast Channel API 沒有緩衝處理,而且在用戶端頁面準備好接收廣播訊息之前,會直接捨棄廣播的訊息。

基於上述原因,我們已將 workbox-broadcast-update 變更為一律在 v5 中使用 postMessage()。目前 Service Worker 範圍內的所有用戶端頁面會逐一送出訊息。

為因應這項新行為,您可以移除在建立 BroadcastChannel 例項的用戶端頁面中擁有的所有程式碼,並改為在 navigator.serviceWorker 上設定 message 事件監聽器:

// v4:
const updatesChannel = new BroadcastChannel('api-updates');
updatesChannel.addEventListener('message', event => {
  const {cacheName, updatedUrl} = event.data.payload;
  // ... your code here ...
});

// v5:
// This listener should be added as early as possible in your page's lifespan
// to ensure that messages are properly buffered.
navigator.serviceWorker.addEventListener('message', event => {
  // Optional: ensure the message came from workbox-broadcast-update
  if (event.meta === 'workbox-broadcast-update') {
    const {cacheName, updatedUrl} = event.data.payload;
    // ... your code here ...
  }
});

workbox-window 使用者不必進行任何變更,因為其內部邏輯已更新,以便監聽 postMessage() 呼叫。

建構工具需要 Node.js v8 以上版本

workbox-webpack-pluginworkbox-buildworkbox-cli 不再支援 v8 之前的 Node.js 版本。如果您使用的 Node.js 版本低於 8,請將執行階段更新為支援的版本

Workbox-webpack-plugin 需要 webpack v4 或以上版本

如果您使用的是 workbox-webpack-plugin,請更新 webpack 設定,使用至少 Webpack v4。

建構工具選項全面升級

系統已不再支援幾個 workbox-buildworkbox-cliworkbox-webpack-plugin 設定參數。舉例來說,generateSW 一律會為您建立本機 Workbox 執行階段套件,所以 importWorkboxFrom 選項已不適用。

如需支援的選項清單,請參閱相關工具的說明文件。

從 Workbox-build 移除 generateSWString

generateSWString 模式已從 workbox-build 中移除。這項更新主要是由 workbox-webpack-plugin 內部使用,因此預期影響不大。

選用變更

使用模組匯入

雖然此變更是) 選用,且 b) 使用 Workbox v4 時,技術上確實可行,但改用 v5 時,我們預期會出現的最大變化,就是藉由匯入 Workbox 模組的方式,建立自己附帶的 Service Worker。這可以做為在服務工作站頂端呼叫 importScripts('/path/to/workbox-sw.js'),以及透過 workbox.* 命名空間使用 Workbox 的替代方法。

如果您是在「產生 SW」模式下使用其中一種建構工具 (workbox-webpack-pluginworkbox-buildworkbox-cli),系統會自動為您進行這項變更。這些工具都會輸出一個 Workbox 執行階段的本機自訂套件,以及實作 Service Worker 邏輯的實際程式碼。在這種情況下,workbox-sw 或 Workbox 的 CDN 副本就不再是依附元件。視您的 inlineWorkboxRuntime 設定值而定,Workbox 執行階段會分割成獨立檔案,此檔案應與您的服務工作站一起部署 (當設為 false 時),或與 Service Worker 邏輯一起內嵌 (如果設為 true)。

如果您在「插入資訊清單」模式下使用建構工具,或完全未使用 Workbox 的建構工具,請參閱現有的「搭配 Workbox 使用 Bundlers (webpack/Rollup)」指南,進一步瞭解如何建立自己的 Workbox 執行階段套件。

我們會假設模組匯入語法來撰寫第 5 版的說明文件和範例,但 Workbox v5 會繼續支援 workbox.* 命名空間。

讀取預先快取回應

有些開發人員必須直接從快取讀取預先快取回應,而不是透過 precacheAndRoute() 方法以隱含方式使用這些回應。在 v4 中,常見的模式是先取得預快取資源目前版本專屬的快取金鑰,然後將該金鑰和預先快取的快取名稱傳入 caches.match(),以取得 Response

為了簡化這項程序,第 5 版中的 workbox-precaching 支援新的對等方法 matchPrecache()

// v4:
import {cacheNames} from 'workbox-core';
import {getCacheKeyForURL} from 'workbox-precaching';

const cachedResponse = await caches.match(
  getCacheKeyForURL(`/somethingPrecached`),
  {
    cacheName: cacheNames.precache,
  }
);

// v5:
import {matchPrecache} from 'workbox-precaching';

const cachedResponse = await matchPrecache(`/somethingPrecached`);

TypeScript 採用率

在 v5 中,Workbox 執行階段程式庫是以 TypeScript 編寫。為協助尚未採用 TypeScript 的開發人員,我們將繼續發布轉譯後的 JavaScript 模組和套件,但如果您使用的是 TypeScript,建議您直接從 Workbox 專案取得準確的最新資訊。

遷移範例

這個修訂版本表示在遷移作業中,有內嵌註解相當牽涉其中。並使用 Rollup 將自訂 Workbox 執行階段納入最終 Service 工作站,而非從 CDN 載入執行階段。

雖然並未涵蓋所有破壞性變更,但以下是將一個 Service Worker 檔案從 v4 升級至 v5 的之前之後,包括切換至 TypeScript。

取得協助

我們預期大部分的遷移作業都很簡單。如果您遇到本指南未涵蓋的問題,請在 GitHub 上建立問題告訴我們。