ワークボックス ウィンドウの使用

このドキュメントでまだあまり説明していないワークボックス モジュールの一つが workbox-window です。これは、window で実行することを想定したモジュールのセットです。このモジュールの目標は次のとおりです。

  • デベロッパーが Service Worker のライフサイクルの重要な瞬間を特定し、その瞬間に簡単に対応できるようにすることで、Service Worker の登録と更新を簡素化します。
  • Service Worker を誤ったスコープで登録するなど、デベロッパーがよくあるミスを犯さないようにするため。
  • windowService Worker のスコープ間のメッセージングを簡素化するため。

workbox-window のインポートと使用

workbox-window から最もよく使用するエクスポートは Workbox クラスです。これは、Node またはウェブページの CDN からインポートできます。

ローカル バンドルの作成

ツールチェーンに webpackRollup などのバンドラが含まれている場合は、workbox-window をローカルにバンドルできます。

まず、アプリの本番環境の依存関係として workbox-window をインストールします。

npm install workbox-window --save

次に、アプリケーションの JavaScript で、workbox-window から Workbox クラスを import できます。

<script type="module">
import {Workbox} from 'workbox-window';

if ('serviceWorker' in navigator) {
  const wb = new Workbox('/sw.js');

  wb.register();
}
</script>

workbox-window は非常に小さいですが、動的 import を使用してウェブサイトのコア アプリケーション ロジックから分割できます。これにより、ページのメインバンドルのサイズを縮小できます。

<script type="module">
if ('serviceWorker' in navigator) {
  const {Workbox} = await import('workbox-window');

  const wb = new Workbox('/sw.js');
  wb.register();
}
</script>

CDN の使用

おすすめのアプローチではありませんが、workbox-window を使用する簡単な方法は CDN からインポートすることです。

<script type="module">
  import {Workbox} from 'https://storage.googleapis.com/workbox-cdn/releases/6.2.0/workbox-window.prod.mjs';

  if ('serviceWorker' in navigator) {
    const wb = new Workbox('/sw.js');

    wb.register();
  }
</script>

上記の例の <script> 要素type="module" 属性が使用されています。これは、ビルドステップなしでブラウザで静的な import ステートメントを使用する場合に必要です。Service Worker をサポートするすべての主要なブラウザは JavaScript モジュールもサポートしています。そのため、古いブラウザは type 属性値が "module"<script> 要素を無視するため、このコードをどのブラウザに配信しても問題ありません。

Service Worker の登録

workbox-window への Service Worker の登録は、次のように Workbox クラスの register メソッドで行われます。

import {Workbox} from 'workbox-window';

const wb = new Workbox('/sw.js');
wb.register();

これは、navigator.serviceWorker.register を使用して Service Worker を自分で登録する場合と同じに思えるかもしれません。ただし、Workbox.register は、Service Worker を登録する前に window load イベントまで待機します。これは、事前キャッシュが関係する状況で望ましい設定です。これにより、ページ起動を遅らせる可能性がある帯域幅の競合を回避できるからです。

window と Service Worker のスコープ間の通信

Service Worker は window とは別に独自のスコープを持ち、window で使用可能な API のサブセットにのみアクセスできます。ただし、window と Service Worker 間の通信は可能です。workbox-window では、workbox-window モジュールの messageSW メソッドにより、2 つのスコープ間の通信が容易になります。

ワークボックスは、次のプロパティを持つオブジェクトとしてメッセージに特定の形式を使用します。

  • type は、メッセージを識別する必須の一意の文字列です。形式は大文字を使用し、単語をアンダースコアで区切る必要があります(例: CACHE_URLS)。
  • meta は、メッセージを送信するワークボックス パッケージの名前を表す文字列です(省略可)。通常は省略されます。
  • payload は、送信するデータを表す省略可能なパラメータです。任意のデータ型を指定できます。

messageSW の動作の例を以下に示します。まず、Service Worker のコードについて説明します。

// sw.js
const SW_VERSION = '1.0.0';

self.addEventListener('message', (event) => {
  if (event.data.type === 'GET_VERSION') {
    event.ports[0].postMessage(SW_VERSION);
  }
});

そして、ウェブページに次のコードを追加します。

const wb = new Workbox('/sw.js');
wb.register();

const swVersion = await wb.messageSW({type: 'GET_VERSION'});
console.log('Service Worker version:', swVersion);

Service Worker と window 間の通信が役立つケースは数多くあります。たとえば、Service Worker のアップデートが利用可能になったときにユーザーに通知します。このレシピは、messageSkipWaiting という self.skipWaiting の特別なヘルパー メソッドに依存しています。このメソッドは type の値が SKIP_WAITING のメッセージを送信します。