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

Service WorkerCache Storage API を使用しているデベロッパーは、Chrome 71 でリリースされる 2 つの小さな変更に注目してください。どちらの変更でも、Chrome の実装は仕様や他のブラウザとより合致しています。

非同期 importScripts() の使用禁止

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

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

ただし、この機能を機能させるには、最初のインストール後に 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));
});

Service Worker のコードは次のようになります。

// 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 のサポート終了

Service Worker とともに Cache Storage API を使用している場合、関連仕様に合わせて Chrome 71 ではもう 1 つの小さな変更があります。cache.addAll() の 1 回の呼び出しで同じ URL が複数回渡された場合、この仕様では、呼び出しによって返される Promise は拒否するように規定されています。

Chrome 71 より前のバージョンではこの検出は検出されず、重複する URL は実質的に無視されました。

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

このロギングは 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 の動作を切り替える前にこの変更についてお伝えしたいと思います。