キャッシュ ストレージは強力なツールです。これにより、アプリがネットワークの状態に依存する程度が軽減されます。キャッシュを適切に使用することで、ウェブアプリをオフラインで利用できるようにし、あらゆるネットワーク状況でアセットをできるだけ高速に提供できます。アセットとデータで説明したように、必要なアセットをキャッシュに保存するための最適な戦略を決定できます。キャッシュを管理するために、Service Worker は Cache Storage API とやり取りします。
Cache Storage API は、次のコンテキストから使用できます。
- ウィンドウ コンテキスト(PWA のメインスレッド)。
- サービス ワーカー。
- 使用しているその他のワーカー。
サービス ワーカーを使用してキャッシュを管理する利点の一つは、そのライフサイクルがウィンドウに関連付けられていないことです。つまり、メインスレッドをブロックしません。Cache Storage API を使用するには、ほとんどのコンテキストで TLS 接続を使用する必要があります。
キャッシュに保存する内容
キャッシュに保存する内容について、まず疑問に思われるかもしれません。この質問に対する明確な答えはありませんが、ユーザー インターフェースのレンダリングに必要な最小限のリソースをすべて用意することをおすすめします。
これらのリソースには、次のものが含まれます。
- メインページの HTML(アプリの start_url)。
- メインのユーザー インターフェースに必要な CSS スタイルシート。
- ユーザー インターフェースで使用される画像。
- ユーザー インターフェースのレンダリングに必要な JavaScript ファイル。
- 基本的なエクスペリエンスをレンダリングするために必要なデータ(JSON ファイルなど)。
- ウェブフォント。
- マルチページ アプリケーションの場合、高速に配信する、またはオフラインで配信する他の HTML ドキュメント。
オフライン対応
オフライン対応はプログレッシブ ウェブアプリの要件の 1 つですが、クラウド ゲーム ソリューションや暗号資産アプリなど、すべての PWA で完全なオフライン エクスペリエンスが必要になるわけではありません。そのため、このような状況でユーザーをガイドする基本的なユーザー インターフェースを提供することは問題ありません。
PWA では、ウェブ レンダリング エンジンがページを読み込めなかったことを示すブラウザのエラー メッセージをレンダリングしないでください。代わりに、サービス ワーカーを使用して独自のメッセージを表示し、一般的なわかりにくいブラウザ エラーを回避します。
PWA のニーズに応じて、さまざまなキャッシュ保存戦略を使用できます。そのため、高速で信頼性の高いエクスペリエンスを提供できるように、キャッシュの使用を設計することが重要です。たとえば、すべてのアプリアセットが高速にダウンロードされ、多くのスペースを消費せず、リクエストごとに更新する必要がない場合は、すべてのアセットをキャッシュに保存するのが有効な戦略です。一方、最新バージョンである必要があるリソースがある場合は、それらのアセットをキャッシュに保存しないことを検討してください。
API の使用
Cache Storage API を使用して、オリジン内の一連のキャッシュを定義します。各キャッシュは、定義できる文字列名で識別されます。caches
オブジェクトを介して API にアクセスします。open
メソッドを使用すると、キャッシュの作成や、すでに作成されているキャッシュの開くことができます。open メソッドは、キャッシュ オブジェクトの Promise を返します。
caches.open("pwa-assets")
.then(cache => {
// you can download and store, delete or update resources with cache arguments
});
アセットのダウンロードと保存
アセットのダウンロードと保存をブラウザにリクエストするには、add
メソッドまたは addAll
メソッドを使用します。add
メソッドはリクエストを行い、1 つの HTTP レスポンスを保存します。addAll
は、リクエストまたは URL の配列に基づいて、HTTP レスポンスをトランザクションとしてグループ化します。
caches.open("pwa-assets")
.then(cache => {
cache.add("styles.css"); // it stores only one resource
cache.addAll(["styles.css", "app.js"]); // it stores two resources
});
キャッシュ ストレージ インターフェースは、すべてのヘッダーと本文を含むレスポンスの全体を保存します。そのため、後で HTTP リクエストまたは URL をキーとして使用して取得できます。その方法については、配信に関する章をご覧ください。
キャッシュを使用する場面
PWA では、ファイルをキャッシュに保存するタイミングを決定するのは開発者です。サービス ワーカーのインストール時にできるだけ多くのアセットを保存する方法もありますが、通常はおすすめしません。不要なリソースをキャッシュに保存すると、帯域幅とストレージ容量が浪費され、アプリで意図しない古いリソースが提供される可能性があります。
すべてのアセットを一度にキャッシュに保存する必要はありません。PWA のライフサイクル中に、次のような方法でアセットを何度もキャッシュに保存できます。
- Service Worker のインストール時。
- 最初のページ読み込み後。
- ユーザーがセクションまたはルートに移動したとき。
- ネットワークがアイドル状態のとき。
新しいファイルのキャッシュ保存は、メインスレッドまたはサービス ワーカー コンテキスト内でリクエストできます。
Service Worker でのアセットのキャッシュ保存
最も一般的なシナリオの 1 つは、サービス ワーカーがインストールされたときに最小限のアセットセットをキャッシュに保存することです。これを行うには、サービス ワーカーの install
イベント内でキャッシュ ストレージ インターフェースを使用します。
サービス ワーカー スレッドはいつでも停止できるため、addAll
プロミスの完了を待つようにブラウザにリクエストすることで、すべてのアセットを保存し、アプリの整合性を維持する機会を増やすことができます。次の例は、サービス ワーカーのイベント リスナーで受信したイベント引数の waitUntil
メソッドを使用して、この処理を行う方法を示しています。
const urlsToCache = ["/", "app.js", "styles.css", "logo.svg"];
self.addEventListener("install", event => {
event.waitUntil(
caches.open("pwa-assets")
.then(cache => {
return cache.addAll(urlsToCache);
});
);
});
waitUntil()
メソッドは Promise を受け取り、Promise 内のタスクが解決(完了または失敗)するまで待ってから Service Worker プロセスを終了するようブラウザに指示します。1 つの結果が waitUntil()
メソッドに渡されるように、Promise を連結して add()
呼び出しまたは addAll()
呼び出しを返す必要がある場合があります。
async/await 構文を使用して Promise を処理することもできます。その場合は、次の例のように、await
を呼び出すことができ、呼び出された後に waitUntil()
にプロミスを返す非同期関数を作成する必要があります。
const urlsToCache = ["/", "app.js", "styles.css", "logo.svg"];
self.addEventListener("install", (event) => {
let cacheUrls = async () => {
const cache = await caches.open("pwa-assets");
return cache.addAll(urlsToCache);
};
event.waitUntil(cacheUrls());
});
クロスドメイン リクエストと不透明なレスポンス
PWA は、オリジンとクロスドメイン(サードパーティの CDN のコンテンツなど)からアセットをダウンロードしてキャッシュに保存できます。クロスドメイン アプリの場合、キャッシュの操作は同一オリジン リクエストと非常によく似ています。リクエストが実行され、レスポンスのコピーがキャッシュに保存されます。他のキャッシュに保存されたアセットと同様に、アプリのオリジンでのみ使用できます。
アセットは不透明なレスポンスとして保存されます。つまり、コードはそのレスポンスのコンテンツやヘッダーを表示または変更できません。また、不透明なレスポンスでは、ストレージ API に実際のサイズが公開されず、割り当てに影響します。一部のブラウザでは、ファイルが 1 KB しかないにもかかわらず、7 MB などの大きなサイズが公開されます。
アセットの更新と削除
アセットの更新には cache.put(request, response)
を使用し、アセットの削除には delete(request)
を使用します。
詳しくは、Cache オブジェクトのドキュメントをご覧ください。
キャッシュ ストレージのデバッグ
多くのブラウザでは、DevTools の [Application] タブでキャッシュ ストレージのコンテンツをデバッグできます。現在のオリジン内のすべてのキャッシュの内容を確認できます。これらのツールの詳細については、ツールとデバッグの章で説明します。