Workbox v4 から v5 に移行する

このガイドでは、Workbox v5 で導入された互換性を破る変更に焦点を当てており、Workbox v4 からアップグレードする際にどのような変更が必要かについて例を挙げています。

互換性を損なう変更

プラグインクラス名の変更

いくつかの Workbox v4 パッケージには、Plugin という名前のクラスが含まれていました。v5 では、これらのクラスは、パッケージ識別子 + 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 の 2 つのオプションの名前を denylistallowlist に変更しました。

workbox-routing は以前、メソッド registerNavigationRoute() をサポートしていました。これは内部で、次の 2 つの処理を行いました。

  1. 指定された fetch イベントの mode'navigate' であるかどうかを示します。
  2. 含まれている場合は、移動先の URL に関係なく、以前にキャッシュしてハードコードされた URL のコンテンツを使用して、そのリクエストに応答します。

これは、App Shell アーキテクチャを実装するときに使用する一般的なパターンです。

キャッシュから読み込んでレスポンスを生成する 2 つ目のステップは、workbox-routing の役割とは見なされません。代わりに、新しいメソッド createHandlerBoundToURL() を介して、workbox-precaching に含めるべき機能と考えることができます。この新しいメソッドは、workbox-routing の既存の NavigationRoute クラスと連携して動作し、同じロジックを実現できます。

ビルドツールの「生成ソフトウェア」モードのいずれかで navigateFallback オプションを使用している場合は、切り替えが自動的に行われます。以前に navigateFallbackBlacklist または navigateFallbackWhitelist オプションを構成している場合は、それぞれ navigateFallbackDenylist または navigateFallbackAllowlist に変更します。

「マニフェストを挿入」モードを使用している場合、または Service Worker を自分で記述していて、Workbox v4 Service Worker が 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);

createHandlerBoundToURL() によって処理されるため、getCacheKeyForURL() を呼び出す必要はありません。

ワークボックス戦略からの makeRequest() の削除

makeRequest() の呼び出しは、workbox-strategy クラスのいずれかで handle() を呼び出すのとほぼ同等です。2 つの方法の違いはごくわずかだったため、両方を保持することは意味がありませんでした。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() を使用するようフォールバックします。

私たちは、2 つの潜在的な受信メッセージの送信元をリッスンしなければならないことに気付きました。そのために、クライアントサイドのコードを記述するのは複雑すぎることがわかりました。さらに、一部のブラウザでは、クライアント ページに送信された Service Worker からの postMessage() 呼び出しは、message イベント リスナーが設定されるまで自動的にバッファリングされます。Broadcast Channel API ではバッファリングは行いません。ブロードキャスト メッセージは、クライアント ページで受信の準備が整う前に送信されただけで破棄されます。

そのため、v5 では常に postMessage() を使用するように workbox-broadcast-update を変更しました。メッセージは、現在の Service Worker の範囲内にあるすべてのクライアント ページに 1 つずつ送信されます。

この新しい動作に対処するには、BroadcastChannel インスタンスを作成したクライアント ページにあるコードをすべて削除し、代わりに navigator.serviceWorkermessage イベント リスナーを設定します。

// 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() の呼び出しをリッスンするように更新されているため、変更する必要はありません。

Build Tools には Node.js v8 以降が必要

v8 より前のバージョンの Node.js は、workbox-webpack-pluginworkbox-buildworkbox-cli でサポートされなくなりました。バージョン 8 より前の Node.js を実行している場合は、ランタイムをサポートされているバージョンに更新してください。

workbox-webpack-plugin には webpack v4 以降が必要です

workbox-webpack-plugin を使用している場合は、webpack の設定を更新して webpack v4 以降を使用するようにしてください。

ビルドツール オプションの見直し

一部の構成パラメータ workbox-buildworkbox-cliworkbox-webpack-plugin がサポートされなくなりました。たとえば、generateSW は常にローカルの Workbox ランタイム バンドルを作成するため、importWorkboxFrom オプションは意味をなさなくなっています。

サポートされているオプションの一覧については、関連するツールのドキュメントをご覧ください。

ワークボックス ビルドから generateSWString を削除

workbox-build から generateSWString モードが削除されました。主に workbox-webpack-plugin によって内部的に使用されていたため、影響は最小限になる見込みです。

オプションの変更

モジュール インポートの使用

この変更は a)オプションであり、b)Workbox v4 を使用している場合には技術的に可能でしたが、v5 への移行中に予想される最大の変更点は、Workbox のモジュールをインポートして独自のバンドルされた Service Worker を作成するモデルです。この方法は、Service Worker の先頭で importScripts('/path/to/workbox-sw.js') を呼び出し、workbox.* 名前空間を介して Workbox を使用する代わりになります。

いずれかのビルドツール(workbox-webpack-pluginworkbox-buildworkbox-cli)を「生成ソフトウェア」モードで使用している場合、この変更は自動的に適用されます。これらのツールはすべて、Service Worker ロジックの実装に必要な実際のコードとともに、Workbox ランタイムのローカルのカスタム バンドルを出力します。このシナリオでは、workbox-sw や Workbox の CDN コピーに対する依存関係がなくなりました。inlineWorkboxRuntime 構成の値に応じて、Workbox ランタイムは、Service Worker とともにデプロイされる個別のファイルに分割されます(false に設定されていて、デフォルトの場合)。または、Service Worker ロジックとともにインラインでデプロイされます(true に設定されている場合)。

「マニフェストを挿入」モードでビルドツールを使用している場合、または Workbox のビルドツールをまったく使用していない場合は、既存の「Bundlers (webpack/Rollup) with Workbox」で、独自の Workbox ランタイム バンドルの作成方法をご確認ください。

v5 のドキュメントと例は、モジュールのインポート構文を前提として作成されていますが、workbox.* 名前空間は Workbox v5 でも引き続きサポートされます。

事前にキャッシュに保存されたレスポンスの読み取り

一部のデベロッパーは、precacheAndRoute() メソッドで暗黙的にレスポンスを使用するのではなく、事前にキャッシュに保存されたレスポンスをキャッシュから直接読み取る必要があります。v4 での一般的なパターンは、まず、事前キャッシュに保存されたリソースの現在のバージョンに固有のキャッシュキーを取得し、そのキーをプリキャッシュのキャッシュ名とともに caches.match() に渡して Response を取得することです。

このプロセスを簡素化するために、v5 の 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 プロジェクトから直接、正確で常に最新の型情報を利用できます。

移行の例

この commit は、インライン コメント付きのかなり複雑な移行を示しています。CDN からランタイムを読み込む代わりに、Rollup を使用して最終的な Service Worker にカスタム Workbox ランタイムを組み込みます。

互換性を破るすべての変更を網羅しているわけではありませんが、TypeScript への切り替えを含む、1 つの Service Worker ファイルを v4 から v5 にアップグレードするについて説明します。

ヘルプ

ほとんどの移行は簡単であると考えられます。このガイドに記載されていない問題が発生した場合は、GitHub で問題を報告してお知らせください。