キャッシュ

キャッシュ ストレージは強力なツールです。アプリがネットワーク状態の影響を受けにくくなります。キャッシュを適切に使用することで、ウェブアプリをオフラインで利用可能にし、あらゆるネットワーク状況でアセットをできるだけ高速に提供できます。アセットとデータで説明したように、必要なアセットをキャッシュに保存するための最適な戦略を決定できます。キャッシュを管理するために、Service Worker は Cache Storage API とやり取りします。

対応ブラウザ

  • Chrome: 43。
  • Edge: 16.
  • Firefox: 41。
  • Safari: 11.1。

ソース

Cache Storage API は、次のコンテキストから使用できます。

  • ウィンドウ コンテキスト(PWA のメインスレッド)。
  • Service Worker。
  • 使用しているその他のワーカー。

サービス ワーカーを使用してキャッシュを管理する利点の一つは、そのライフサイクルがウィンドウに関連付けられていないことです。つまり、メインスレッドをブロックしません。Cache Storage API を使用するには、これらのコンテキストのほとんどを TLS 接続の下に置く必要があります。

キャッシュに保存する内容

キャッシュに保存する内容について、まず疑問に思われるかもしれません。この質問に対する明確な答えはありませんが、ユーザー インターフェースのレンダリングに必要な最小限のリソースをすべて用意することをおすすめします。

これらのリソースには、次のものが必要です。

  • メインページの HTML(アプリの start_url)。
  • メインのユーザー インターフェースに必要な CSS スタイルシート。
  • ユーザー インターフェースで使用される画像。
  • ユーザー インターフェースのレンダリングに必要な JavaScript ファイル。
  • 基本的なエクスペリエンスを表示するために必要なデータ(JSON ファイルなど)。
  • ウェブフォント。
  • マルチページ アプリケーションの場合、高速に配信する、またはオフラインで配信する他の HTML ドキュメント。

オフライン対応

オフライン対応はプログレッシブ ウェブアプリの要件の 1 つですが、クラウド ゲーム ソリューションや暗号資産アプリなど、すべての PWA で完全なオフライン エクスペリエンスが必要になるわけではありません。そのため、このような状況でユーザーをガイドする基本的なユーザー インターフェースを提供することは問題ありません。

ウェブ レンダリング エンジンがページを読み込めなかったことを示すブラウザのエラー メッセージが PWA でレンダリングされないようにする必要があります。代わりに Service Worker を使用して独自のメッセージを表示することで、一般的で混乱を招くブラウザエラーを回避できます。

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 では、ファイルをキャッシュに保存するタイミングを決定するのは開発者です。1 つの方法は、Service Worker のインストール時にできるだけ多くのアセットを保存することですが、通常はおすすめしません。不要なリソースをキャッシュに保存すると、帯域幅とストレージ容量が浪費され、アプリで意図しない古いリソースが提供される可能性があります。

すべてのアセットを一度にキャッシュに保存する必要はありません。PWA のライフサイクル中に、次のような方法でアセットを何度もキャッシュに保存できます。

  • Service Worker のインストール時。
  • 最初のページが読み込まれた後。
  • ユーザーが特定のセクションまたはルートに移動したとき。
  • ネットワークがアイドル状態の場合。

新しいファイルのキャッシュ保存は、メインスレッドまたはサービス ワーカーのコンテキスト内でリクエストできます。

Service Worker でのアセットのキャッシュ保存

最も一般的なシナリオの 1 つは、サービス ワーカーがインストールされたときに最小限のアセットセットをキャッシュに保存することです。そのためには、Service Worker の 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 プロセスを終了するようブラウザに指示します。単一の結果が 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 のコンテンツなど)からアセットをダウンロードしてキャッシュに保存できます。クロスドメイン アプリの場合、キャッシュの操作は同一オリジン リクエストと非常によく似ています。リクエストが実行され、レスポンスのコピーがキャッシュに保存されます。他のキャッシュに保存されたアセットと同様に、アプリのオリジンでのみ使用できます。

アセットは不透明なレスポンスとして保存されます。つまり、コードでそのレスポンスのコンテンツやヘッダーを表示または変更することはできません。また、不透明なレスポンスは Storage API で実際のサイズを公開せず、割り当てに影響します。一部のブラウザでは、ファイルが 1 KB しかないにもかかわらず、7 MB などの大きなサイズが公開されます。

アセットの更新と削除

cache.put(request, response) を使用してアセットを更新し、delete(request) を使用してアセットを削除できます。

詳しくは、Cache オブジェクトのドキュメントをご覧ください。

キャッシュ ストレージのデバッグ

多くのブラウザでは、DevTools の [Application] タブでキャッシュ ストレージのコンテンツをデバッグできます。現在のオリジン内のすべてのキャッシュの内容を確認できます。これらのツールの詳細については、ツールとデバッグの章で説明します。

キャッシュ ストレージのコンテンツをデバッグする Chrome DevTools。

リソース