cache.addAll() と importScripts() を微調整(Chrome 71)

サービス ワーカーCache Storage API を使用するデベロッパーは、Chrome 71 で展開される 2 つの小さな変更に注意する必要があります。どちらの変更も、Chrome の実装を仕様と他のブラウザに近づけるものです。

非同期 importScripts() の禁止

importScripts() は、メインの Service Worker スクリプトに、現在の実行を一時停止し、指定された URL から追加のコードをダウンロードして、現在のグローバル スコープで完了まで実行するよう指示します。完了すると、メイン サービス ワーカー スクリプトの実行が再開されます。importScripts() は、組織上の理由でメインのサービス ワーカー スクリプトを小さな部分に分割する場合や、サードパーティ コードをプルしてサービス ワーカーに機能を追加する場合に便利です。

ブラウザは、importScripts() を介して pull されたものを自動的にキャッシュに保存することで、「同期コードをダウンロードして実行する」というパフォーマンスの問題を軽減しようとします。つまり、最初のダウンロード後、インポートされたコードの実行に伴うオーバーヘッドはほとんどありません。

ただし、この機能が機能するには、最初のインストール後に Service Worker に「予期しない」コードがインポートされないことをブラウザが認識する必要があります。Service Worker の仕様では、importScripts() の呼び出しは、トップレベルの Service Worker スクリプトの同期実行中、または必要に応じて install ハンドラ内で非同期にのみ機能することが想定されています。

Chrome 71 より前は、install ハンドラの外部で importScripts() を非同期で呼び出すと機能していました。Chrome 71 以降では、これらの呼び出しは、他のブラウザの動作と一致して、(同じ URL が install ハンドラで以前にインポートされていない限り)ランタイム例外をスローします。

次のようなコードではなく、

// This only works in Chrome 70 and below.
self.addEventListener('fetch', event => {
  importScripts('my-fetch-logic.js');
  event.respondWith(self.customFetchLogic(event));
});

サービス ワーカーのコードは次のようになります。

// Move the importScripts() to the top-level scope.
// (Alternatively, import the same URL in the install handler.)
importScripts('my-fetch-logic.js');
self.addEventListener('fetch', event => {
  event.respondWith(self.customFetchLogic(event));
});

cache.addAll() に渡される重複する URL の非推奨

Cache Storage API をサービス ワーカーとともに使用している場合、Chrome 71 では関連する仕様に合わせて、別の小さな変更も行われています。同じ URL が 1 回の cache.addAll() 呼び出しに複数回渡された場合、呼び出しから返された Promise は拒否される必要があります。

Chrome 71 より前は、この問題は検出されず、重複する URL は事実上無視されていました。

Chrome コンソールに表示された警告メッセージのスクリーンショット
Chrome 71 以降では、コンソールに警告メッセージがログに記録されます。

このロギングは Chrome 72 の前兆です。Chrome 72 では、重複する URL がロギングされた警告ではなく、cache.addAll() の拒否につながります。一般的な方法として、InstallEvent.waitUntil() に渡される Promise チェーンの一部として cache.addAll() を呼び出す場合、この拒否により Service Worker のインストールに失敗する可能性があります。

次のような問題が発生する可能性があります。

const urlsToCache = [
  '/index.html',
  '/main.css',
  '/app.js',
  '/index.html', // Oops! This is listed twice and should be removed.
];

self.addEventListener('install', event => {
  event.waitUntil(
    caches.open('my-cache').then(cache => cache.addAll(urlsToCache))
  );
});

この制限は、cache.addAll() に渡される実際の URL にのみ適用されます。最終的に URL が異なる 2 つの同等のレスポンス('/''/index.html' など)をキャッシュに保存しても、拒否はトリガーされません。

Service Worker の実装を幅広くテストする

現時点では、Service Worker はすべての主要な「エバーグリーン」ブラウザ広く実装されています。多数のブラウザでプログレッシブ ウェブアプリを定期的にテストしている場合や、Chrome を使用していないユーザーが多数いる場合は、すでに不一致を検出してコードを更新している可能性があります。ただし、他のブラウザでこの動作に気付いていない可能性もあるため、Chrome の動作を変更する前に、この変更についてお知らせいたします。