瞭解如何使用 Cache API,將應用程式資料設為可離線使用。
Cache API 是用於儲存及擷取網路要求及其對應回應的系統。這些來源可能是在執行應用程式時產生的一般要求和回應,或是只用來儲存資料供日後使用。
建立 Cache API,可讓服務工作站快取網路要求,無論網路速度或可用性為何,都能提供快速回應。不過,這個 API 也可以用來當做一般的儲存機制。
點此查看服務地區
所有新版瀏覽器都能使用 Cache API。這項資訊會透過全域 caches
屬性公開,因此您可以透過簡單的功能偵測來測試 API 的是否存在:
const cacheAvailable = 'caches' in self;
Cache API 可從視窗、iframe、工作站或 Service 工作站存取。
可儲存的內容
快取只會儲存 Request
和 Response
物件的組合,分別代表 HTTP 要求和回應。然而,要求和回應可包含任何可透過 HTTP 傳輸的資料。
可儲存多少儲存空間?
簡單來說,至少會有數百 MB 或幾百 GB 以上。瀏覽器實作方式不盡相同,但可用的儲存空間量通常取決於裝置可用的儲存空間大小。
建立及開啟快取
如要開啟快取,請使用 caches.open(name)
方法,並將快取名稱做為單一參數傳遞。如果命名的快取不存在,則會建立該快取。這個方法會傳回能與 Cache
物件解析的 Promise
。
const cache = await caches.open('my-cache');
// do something with cache...
新增至快取
將項目新增至快取的方式有三種:add
、addAll
和 put
。這三個方法都會傳回 Promise
。
cache.add
首先是 cache.add()
。這個方法會使用一個參數 (Request
或網址 (string
)。這會向網路發出要求,並將回應儲存在快取中。如果擷取失敗,或是回應的狀態碼不在 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
物件或網址 (string
) 的陣列。這與為每個個別要求呼叫 cache.add
的做法相似,除非未快取任何單一要求,Promise
則會拒絕。
const urls = ['/weather/today.json', '/weather/tomorrow.json'];
cache.addAll(urls);
在這些情況下,新的項目會覆寫任何相符的現有項目。這會使用與retrieving一節中所述相同的比對規則。
cache.put
最後,cache.put()
可讓您儲存來自網路的回應,或建立及儲存自己的 Response
。這會使用兩個參數。第一個是 Request
物件或網址 (string
)。
第二個必須是 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 物件
使用儲存項目的網址建立 Request
物件:
const request = new Request('/my-data-store/item-id');
使用回應物件
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
。如果找到相符的項目,函式會傳回 Promise
,該函式會解析為 Response
,否則會傳回 undefined
。
為判斷兩個 Requests
是否相符,瀏覽器不只使用網址,如果兩個要求有不同的查詢字串、Vary
標頭或 HTTP 方法 (GET
、POST
、PUT
等),就會視為不同的要求。
您可以將選項物件做為第二個參數傳遞,藉此忽略部分或全部內容。
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.`);
您也可以使用 caches.match()
一次搜尋所有快取,而不必針對每個快取呼叫 cache.match()
。
搜尋中
除了與 Response
物件相符的項目以外,Cache API 不提供搜尋要求或回應的方式,不過,您可以使用篩選條件或建立索引來實作自己的搜尋。
篩選
實作自己的搜尋的方法之一,就是疊代所有項目,然後篩選到需要的項目。假設您想找出所有網址結尾是 .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
物件的任何屬性來篩選項目。請注意,搜尋大型資料集時,速度會比較慢。
建立索引
另一種實作自己的搜尋方式,是維持可供搜尋的項目個別索引,並將索引儲存在索引資料庫中。因為這正是 IndexedDB 專為使用大量項目而設計的作業。
如果您將 Request
的網址與可搜尋的屬性一起儲存,就能在搜尋後輕鬆擷取正確的快取項目。
刪除項目
如何刪除快取中的項目:
cache.delete(request);
其中要求可以是 Request
或網址字串。這個方法也使用與 cache.match
相同的選項物件,可讓您刪除同一個網址的多個 Request
/Response
組合。
cache.delete('/example/file.txt', {ignoreVary: true, ignoreSearch: true});
刪除快取
如要刪除快取,請呼叫 caches.delete(name)
。如果快取存在且已刪除,這個函式會傳回 Promise
,否則會傳回 true
;否則會傳回 false
。
感謝
感謝 Mat Scales 協助撰寫本文的原始版本,該版本首次出現在 WebFundamentals 上。