Cache API を使用して、アプリケーション データをオフラインで利用できるようにする方法について学びます。
Cache API は、ネットワーク リクエストとそれに対応するレスポンスを保存、取得するためのシステムです。これらは、アプリケーションの実行中に作成された通常のリクエストとレスポンスである場合もあれば、後で使用するためにデータを保存する目的でのみ作成される場合もあります。
Cache API は、サービス ワーカーがネットワーク リクエストをキャッシュに保存し、ネットワークの速度や可用性に関係なく高速なレスポンスを提供できるようにするために作成されました。ただし、この API は一般的なストレージ メカニズムとしても使用できます。
どこで利用できますか?
Cache API は、すべての最新ブラウザで利用できます。これはグローバル caches
プロパティを介して公開されるため、簡単な機能検出で API の有無をテストできます。
const cacheAvailable = 'caches' in self;
Cache API には、ウィンドウ、iframe、ワーカー、Service Worker からアクセスできます。
保存できる内容
キャッシュには、HTTP リクエストとレスポンスを表す Request
オブジェクトと Response
オブジェクトのペアのみが保存されます。ただし、リクエストとレスポンスには、HTTP 経由で転送できるあらゆる種類のデータを含めることができます。
保存できるデータの量はどれくらいですか?
つまり、非常に多くの、少なくとも数百 MB、場合によっては数百 GB 以上の容量が必要です。ブラウザの実装はさまざまですが、使用可能なストレージの量は通常、デバイスで使用可能なストレージの量に基づいています。
キャッシュの作成と開く
キャッシュを開くには、caches.open(name)
メソッドを使用して、キャッシュの名前を単一のパラメータとして渡します。名前付きキャッシュが存在しない場合、キャッシュは作成されます。このメソッドは、Cache
オブジェクトで解決される Promise
を返します。
const cache = await caches.open('my-cache');
// do something with cache...
キャッシュへの追加
アイテムをキャッシュに追加する方法は add
、addAll
、put
の 3 つあります。3 つのメソッドはすべて Promise
を返します。
cache.add
まず、cache.add()
があります。Request
または URL(string
)のいずれかのパラメータを 1 つ受け取ります。ネットワークにリクエストを行い、レスポンスをキャッシュに保存します。フェッチが失敗した場合、またはレスポンスのステータス コードが 200 の範囲外の場合、何も保存されず、Promise
は拒否されます。CORS モードではないクロスオリジン リクエストは、0
の status
を返すため、保存できません。このようなリクエストは put
でのみ保存できます。
// Retreive data.json from the server and store the response.
cache.add(new Request('/data.json'));
// Retreive data.json from the server and store the response.
cache.add('/data.json');
cache.addAll
次は cache.addAll()
です。add()
と同様ですが、Request
オブジェクトまたは URL(string
)の配列を受け取ります。これは、個々のリクエストごとに cache.add
を呼び出す場合と同様ですが、単一のリクエストがキャッシュに保存されていない場合、Promise
は拒否します。
const urls = ['/weather/today.json', '/weather/tomorrow.json'];
cache.addAll(urls);
いずれの場合も、新しいエントリが一致する既存のエントリを上書きします。これには、取得のセクションで説明したマッチング ルールが使用されます。
cache.put
最後に、cache.put()
があります。これを使用すると、ネットワークからのレスポンスを保存したり、独自の Response
を作成して保存したりできます。2 つのパラメータを取ります。1 つ目は、Request
オブジェクトまたは URL(string
)のいずれかです。2 つ目は、ネットワークから取得するか、コードで生成した Response
である必要があります。
// Retrieve data.json from the server and store the response.
cache.put('/data.json');
// Create a new entry for test.json and store the newly created response.
cache.put('/test.json', new Response('{"foo": "bar"}'));
// Retrieve data.json from the 3rd party site and store the response.
cache.put('https://example.com/data.json');
put()
メソッドは add()
や addAll()
よりも許容範囲が広く、CORS 以外のレスポンスや、レスポンスのステータス コードが 200 範囲にない他のレスポンスを保存できます。同じリクエストの以前のレスポンスは上書きされます。
Request オブジェクトを作成する
保存されるものの URL を使用して Request
オブジェクトを作成します。
const request = new Request('/my-data-store/item-id');
Response オブジェクトの操作
Response
オブジェクトのコンストラクタは、Blob
、ArrayBuffer
、FormData
オブジェクト、文字列など、さまざまな種類のデータを受け入れます。
const imageBlob = new Blob([data], {type: 'image/jpeg'});
const imageResponse = new Response(imageBlob);
const stringResponse = new Response('Hello world');
Response
の MIME タイプは、適切なヘッダーを設定することで設定できます。
const options = {
headers: {
'Content-Type': 'application/json'
}
}
const jsonResponse = new Response('{}', options);
Response
を取得してその本文にアクセスする場合は、使用できるヘルパー メソッドがいくつかあります。それぞれ、異なる型の値で解決される Promise
を返します。
メソッド | 説明 |
---|---|
arrayBuffer |
バイトにシリアル化された本文を含む ArrayBuffer を返します。 |
blob |
Blob を返します。Response が Blob で作成された場合、この新しい Blob は同じ型になります。それ以外の場合は、Response の Content-Type が使用されます。 |
text |
本文のバイト値を UTF-8 エンコードされた文字列として解釈します。 |
json |
本文のバイト列を UTF-8 エンコード文字列として解釈し、JSON として解析しようとします。結果のオブジェクトを返します。文字列を JSON として解析できない場合は、TypeError をスローします。
|
formData |
本文のバイト列を HTML フォームとして解釈します。このフォームは multipart/form-data または application/x-www-form-urlencoded のいずれかでエンコードされます。FormData オブジェクトを返します。データを解析できない場合は TypeError をスローします。 |
body |
本文データの ReadableStream を返します。 |
次に例を示します。
const response = new Response('Hello world');
const buffer = await response.arrayBuffer();
console.log(new Uint8Array(buffer));
// Uint8Array(11) [72, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100]
キャッシュからの取得
キャッシュ内のアイテムを見つけるには、match
メソッドを使用します。
const response = await cache.match(request);
console.log(request, response);
request
が文字列の場合、ブラウザは new Request(request)
を呼び出して Request
に変換します。この関数は、一致するエントリが見つかった場合は Response
に解決される Promise
を返します。一致しない場合は undefined
を返します。
2 つの Requests
が一致するかどうかを判断するために、ブラウザは URL 以外の要素も使用します。2 つのリクエストが異なると見なされるのは、クエリ文字列、Vary
ヘッダー、HTTP メソッド(GET
、POST
、PUT
など)が異なる場合です。
これらの処理の一部またはすべてを無視するには、オプション オブジェクトを 2 番目のパラメータとして渡します。
const options = {
ignoreSearch: true,
ignoreMethod: true,
ignoreVary: true
};
const response = await cache.match(request, options);
// do something with the response
キャッシュに保存された複数のリクエストが一致する場合は、最初に作成されたリクエストが返されます。一致するレスポンスをすべて取得する場合は、cache.matchAll()
を使用します。
const options = {
ignoreSearch: true,
ignoreMethod: true,
ignoreVary: true
};
const responses = await cache.matchAll(request, options);
console.log(`There are ${responses.length} matching responses.`);
ショートカットとして、キャッシュごとに cache.match()
を呼び出すのではなく、caches.match()
を使用してすべてのキャッシュを一度に検索できます。
検索機能
Cache API には、Response
オブジェクトとエントリを照合する以外に、リクエストやレスポンスを検索する方法はありません。ただし、フィルタリングを使用するか、インデックスを作成することで、独自の検索を実装できます。
フィルタリング
独自の検索を実装する方法の一つは、すべてのエントリを反復処理して、目的のエントリに絞り込むことです。たとえば、URL が .png
で終わるすべてのアイテムを検索するとします。
async function findImages() {
// Get a list of all of the caches for this origin
const cacheNames = await caches.keys();
const result = [];
for (const name of cacheNames) {
// Open the cache
const cache = await caches.open(name);
// Get a list of entries. Each item is a Request object
for (const request of await cache.keys()) {
// If the request URL matches, add the response to the result
if (request.url.endsWith('.png')) {
result.push(await cache.match(request));
}
}
}
return result;
}
これにより、Request
オブジェクトと Response
オブジェクトの任意のプロパティを使用してエントリをフィルタできます。大規模なデータセットを検索する場合は、時間がかかる点に注意してください。
インデックスの作成
独自の検索を実装するもう 1 つの方法は、検索可能なエントリの個別のインデックスを維持し、そのインデックスを IndexedDB に保存することです。これは IndexedDB が設計された種類の操作であるため、エントリが大量にある場合でもパフォーマンスが大幅に向上します。
検索可能なプロパティとともに Request
の URL を保存すると、検索後に正しいキャッシュ エントリを簡単に取得できます。
アイテムの削除
キャッシュからアイテムを削除するには:
cache.delete(request);
ここで、リクエストは Request
または URL 文字列です。このメソッドは cache.match
と同じオプション オブジェクトも受け取るため、同じ URL の複数の Request
/Response
ペアを削除できます。
cache.delete('/example/file.txt', {ignoreVary: true, ignoreSearch: true});
キャッシュの削除
キャッシュを削除するには、caches.delete(name)
を呼び出します。この関数は、キャッシュが存在し、削除された場合は true
に解決する Promise
を返します。それ以外の場合は false
を返します。
ありがとう
この記事のオリジナル バージョンを作成した Mat Scales に感謝します。このバージョンは、最初に WebFundamentals に掲載されました。