Cache

Baseline Widely available

This feature is well established and works across many devices and browser versions. It’s been available across browsers since April 2018.

Cache インターフェイスは、 Request / Response オブジェクトのペアを保存するストレージの仕組みを提供します。Cache オブジェクトがどのくらい持続するかはブラウザーに依存しますが、単一のオリジンのスクリプトは通常、以前に生成された Cache オブジェクトの存在を頼りにすることができます。Cache インターフェイスは、ワーカーと同様にウィンドウスコープにも公開されていることに注意してください。サービスワーカーの仕様で定義されているとはいえ、サービスワーカーと共に使用する必要はありません。

ひとつのオリジンが、複数の名前付き Cache オブジェクトを保有することができます。スクリプト(例えば ServiceWorker) が Cache の更新をどのように処理するかを実装するのは作者の責任です。Cache 内のアイテムは、明示的に要求されない限り更新されませんし、削除されない限り有効期限はありません。 CacheStorage.open() を使用して特定の名前付き Cache オブジェクトを開き、それから任意の Cache のメソッドを呼び出して Cache を管理します。

また、定期的にキャッシュ項目を掃除する必要があります。各ブラウザーは、あるオリジンで使用できるキャッシュストレージの総量に厳しい制限を設けています。 Cache 容量の概算の使用量は StorageManager.estimate() API を用いて確認することができます。ブラウザーはディスク容量の管理に最善を尽くしますが、あるオリジンのキャッシュストレージを削除することがあります。ブラウザーはふつう、あるオリジンのデータをすべて削除するか、まったく削除しないかのいずれかです。名前を用いてキャッシュをバージョン管理し、安全に操作できるスクリプトのバージョンからのみキャッシュを使用するようにしてください。詳細は、古いキャッシュの削除を確認してください。

メモ: キーの照合アルゴリズムは、値の中にある VARY ヘッダーに依存しています。そのため、新しいキーを照合するには、キャッシュ内の項目のキーと値の両方を調べる必要があります。

メモ: キャッシュ API は HTTP のキャッシュヘッダーを尊重しません。

メモ: この機能はウェブワーカー内で利用可能です。

安全なコンテキスト用: この機能は一部またはすべての対応しているブラウザーにおいて、安全なコンテキスト (HTTPS) でのみ利用できます。

インスタンスメソッド

Cache.match(request, options)

Cache オブジェクト内で最初に一致したリクエストに関連するレスポンスで解決する Promise を返します。

Cache.matchAll(request, options)

Cache オブジェクト内で一致したすべてのレスポンスの配列で解決する Promise を返します。

Cache.add(request)

URL を受け取り、それを取得して、指定されたキャッシュに結果のレスポンスオブジェクトを追加します。機能的には fetch() を呼び出してから、 put() を使用してキャッシュに結果を追加するのと同等です。

Cache.addAll(requests)

URL の配列を受け取り、それらを取得して指定されたキャッシュに結果のレスポンスオブジェクトを追加します。

Cache.put(request, response)

リクエストとそのレスポンスの両方を受け取り、指定されたキャッシュへ追加します。

Cache.delete(request, options)

request をキーとした Cache 項目を探し、一致する Cache 項目が見つかった場合は削除して true で解決し、Cache 項目が見つからなかった場合は false で解決する Promise を返します。

Cache.keys(request, options)

Cache キーの配列で解決する Promise を返します。

このコードスニペットは、service worker selective caching sample からのものです(selective caching をライブで見る)。このコードでは CacheStorage.open() を使用して、 font/ で始まる Content-Type ヘッダー用の Cache オブジェクトを開きます。

そしてこのコードは、 Cache.match() を使用してすでにキャッシュ内に一致するフォントがあるかどうかを確認し、もしあれば、それを返します。一致するフォントがなかった場合は、コードはネットワークからフォントを取得して、 Cache.put() を用いて取得したリソースをキャッシュします。

このコードは fetch() の操作で発生する例外を処理します。なお、 HTTP のエラーレスポンス(404 など)はこの例外を発生させません。適切なエラーコードを持つ通常のレスポンスオブジェクトを返します。

このコードスニペットでは、サービスワーカーで使用されるバージョン付きキャッシュのベストプラクティスも示しています。この例ではキャッシュが 1 つしかありませんが、キャッシュが複数でも同じアプローチが利用できます。これはキャッシュの一括指定識別子を、具体的なバージョン付けされたキャッシュ名に対応させます。このコードはまた、 CURRENT_CACHES で名前が指定されていないキャッシュをすべて削除します。

このコード例において、 cachesServiceWorkerGlobalScope のプロパティです。これは CacheStorage オブジェクトを保持し、 CacheStorage インターフェイスでアクセスすることができます。

メモ: Chrome では、 chrome://inspect/#service-workers にアクセスして、登録されたサービスワーカーの下の "inspect" リンクをクリックすると、 service-worker.js スクリプトが行う様々なアクションのログ状態を見ることができます。

js
const CACHE_VERSION = 1;
const CURRENT_CACHES = {
  font: `font-cache-v${CACHE_VERSION}`,
};

self.addEventListener("activate", (event) => {
  // CURRENT_CACHES で指定されていないすべてのキャッシュを削除します。
  // この例ではキャッシュは 1 つしかありませんが、同じロジックで
  // 複数のバージョン化されたキャッシュがある場合も処理できます。
  const expectedCacheNamesSet = new Set(Object.values(CURRENT_CACHES));
  event.waitUntil(
    caches.keys().then((cacheNames) =>
      Promise.all(
        cacheNames.map((cacheName) => {
          if (!expectedCacheNamesSet.has(cacheName)) {
            // このキャッシュ名が「予期される」キャッシュ名のセットに
            // 存在しない場合は、削除します。
            console.log("期限切れのキャッシュを削除:", cacheName);
            return caches.delete(cacheName);
          }
        }),
      ),
    ),
  );
});

self.addEventListener("fetch", (event) => {
  console.log("Handling fetch event for", event.request.url);

  event.respondWith(
    caches.open(CURRENT_CACHES.font).then((cache) => {
      return cache
        .match(event.request)
        .then((response) => {
          if (response) {
            // event.request の項目がキャッシュにある場合、レスポンスが定義され、
            // それを返すことができます。
            // この例では、フォントリソースのみがキャッシュされることに注意してください。
            console.log(" キャッシュ内にレスポンスあり:", response);

            return response;
          }

          // それ以外の場合、event.request の項目がキャッシュにない場合、
          // レスポンスは undefined となり、リソースを fetch() する必要があります。
          console.log(
            " キャッシュ内に %s のレスポンスが見つかりません。" +
              "ネットワークから取得します…",
            event.request.url,
          );

          // 後で cache.put() の呼び出しで使用する可能性があるため、
          // リクエストで .clone() を呼び出します。
          // fetch() と cache.put() の両方がリクエストを「消費」するため、
          // コピーを作成する必要があります。
          // (https://developer.mozilla.org/ja/docs/Web/API/Request/clone を参照)
          return fetch(event.request.clone()).then((response) => {
            console.log(
              "  ネットワークからの %s のレスポンス: %O",
              event.request.url,
              response,
            );

            if (
              response.status < 400 &&
              response.headers.has("content-type") &&
              response.headers.get("content-type").match(/^font\//i)
            ) {
              // これにより、エラーであることがわかっているレスポンス(つまり、
              // HTTP ステータスコード 4xx または 5xx)のキャッシュが回避されます。
              // また、フォントに対応するレスポンス、すなわち "font/" で始まる
              // Content-Type レスポンスヘッダーを持つもののみをキャッシュする
              // ようにします。
              // なお、不透明なフィルターされたレスポンス
              // https://fetch.spec.whatwg.org/#concept-filtered-response-opaque
              // の場合はレスポンスヘッダーにアクセスできないので、このチェックは
              // 常に失敗し、フォントはキャッシュされません。
              // すべての Google Web Fonts は CORS をサポートするドメインから
              // 提供されるため、ここでは問題になりません。
              // ただし、CORS をサポートしていない他のオリジンのドメインから他の
              // リソースをキャッシュしようとしている場合は、注意が必要です。
              console.log("  レスポンスをキャッシュ: ", event.request.url);
              // レスポンスに対して .clone() を呼び出して、そのコピーを
              // キャッシュに保存します。そうすることで、制御されたページに戻る
              // 元のレスポンスオブジェクトを保持することができます。
              // https://developer.mozilla.org/ja/docs/Web/API/Request/clone
              cache.put(event.request, response.clone());
            } else {
              console.log(
                "  レスポンスをキャッシュしない: ",
                event.request.url,
              );
            }

            // 元のレスポンスオブジェクトを返します。これは、リソース要求を満たすために使用されます。
            return response;
          });
        })
        .catch((error) => {
          // この catch() は、match() または fetch() 操作から発生する
          // 例外を処理します。
          // HTTP エラーレスポンス(404 など)は例外を起動しないことに
          // 注意してください。
          // 適切なエラーコードが設定された、通常のレスポンスオブジェクトを
          // 返します。
          console.error("  フェッチハンドラーのエラー:", error);

          throw error;
        });
    }),
  );
});

クッキーとキャッシュオブジェクト

フェッチ API では Set-Cookie ヘッダーを、 Response オブジェクトを fetch() から返す前に削除することを要求しています。したがって、 ResponseCache へ格納されるとき Set=Cookie ヘッダーを含まず、クッキーを格納する操作も行われません。

仕様書

Specification
Service Workers
# cache-interface

ブラウザーの互換性

BCD tables only load in the browser

関連情報