キャッシュ ストレージは強力なツールです。これにより、アプリがネットワークの状況に依存しなくなります。キャッシュを適切に使用すると、ウェブアプリをオフラインで利用できるようにし、ネットワークの状況に関係なくアセットをできるだけ速く配信できます。アセットとデータで説明したように、必要なアセットをキャッシュに保存する最適な戦略を決定できます。サービス ワーカーがやり取りするキャッシュを管理するには、Cache Storage API を使用します。
Cache Storage API は、さまざまなコンテキストで使用できます。
- ウィンドウ コンテキスト(PWA のメインスレッド)。
- サービス ワーカー。
- 使用している他のワーカー。
サービス ワーカーを使用してキャッシュを管理する利点の 1 つは、ライフサイクルがウィンドウに関連付けられていないため、メインスレッドをブロックしないことです。Cache Storage API を使用するには、これらのコンテキストのほとんどが TLS 接続下にある必要があります。
キャッシュに保存する内容
キャッシュ保存について最初に疑問に思うのは、何をキャッシュに保存するかでしょう。この質問に対する単一の答えはありませんが、ユーザー インターフェースをレンダリングするために必要な最小限のリソースから始めることができます。
これらのリソースには、次のものが含まれます。
- メインページの HTML(アプリの start_url)。
- メインのユーザー インターフェースに必要な CSS スタイルシート。
- ユーザー インターフェースで使用される画像。
- ユーザー インターフェースのレンダリングに必要な JavaScript ファイル。
- 基本的なエクスペリエンスをレンダリングするために必要なデータ(JSON ファイルなど)。
- ウェブフォント。
- マルチページ アプリケーションで、高速に提供したい、またはオフラインで提供したい他の HTML ドキュメント。
オフライン対応
オフライン対応はプログレッシブ ウェブアプリの要件の 1 つですが、クラウド ゲーミング ソリューションや暗号資産アプリなど、すべての PWA で完全なオフライン エクスペリエンスが必要なわけではないことを理解しておくことが重要です。そのため、ユーザーがそのような状況を乗り越えるための基本的なユーザー インターフェースを提供しても構いません。
PWA で、ウェブ レンダリング エンジンがページを読み込めなかったことを示すブラウザのエラー メッセージが表示されないようにします。代わりに、サービス ワーカーを使用して独自のメッセージを表示し、汎用的でわかりにくいブラウザ エラーを回避します。
PWA のニーズに応じて、さまざまなキャッシュ保存戦略を使用できます。そのため、高速で信頼性の高いエクスペリエンスを提供できるようにキャッシュの使用を設計することが重要です。たとえば、アプリのアセットがすべて高速でダウンロードされ、多くのスペースを消費せず、リクエストごとに更新する必要がない場合は、すべてのアセットをキャッシュに保存することが有効な戦略となります。一方、最新バージョンにする必要があるリソースがある場合は、それらのアセットをまったくキャッシュに保存しないことを検討してください。
API の使用
Cache Storage API を使用して、オリジン内に一連のキャッシュを定義します。各キャッシュは、定義可能な文字列名で識別されます。API には caches
オブジェクトを介してアクセスします。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 のタスクが解決(成功または失敗)するまで待機するよう指示します。単一の結果が waitUntil()
メソッドに届くように、Promise を連結して add()
または addAll()
呼び出しを返す必要がある場合があります。
async/await 構文を使用して Promise を処理することもできます。その場合は、次の例のように、await
を呼び出すことができ、呼び出された後に waitUntil()
に Promise を返す非同期関数を作成する必要があります。
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)
を使用します。
詳しくは、キャッシュ オブジェクトのドキュメントをご覧ください。
キャッシュ ストレージのデバッグ
多くのブラウザでは、DevTools の [アプリケーション] タブでキャッシュ ストレージの内容をデバッグできます。ここでは、現在のオリジン内のすべてのキャッシュの内容を確認できます。これらのツールについては、ツールとデバッグの章で詳しく説明します。