Cache API:快速指南

瞭解如何使用 Cache API,讓應用程式資料可供離線使用。

Cache API 是用於儲存及擷取網路要求及其對應回應的系統。這些要求和回應可能是在應用程式的執行過程中所建立的一般要求和回應,也可能只為了儲存資料供日後使用。

Cache API 的建立目的是讓服務工作者快取網路要求,無論網路速度或可用性如何,都能提供快速回應。不過,該 API 也可做為一般儲存機制使用。

點此查看服務地區

Cache API 適用於所有新式瀏覽器。它是透過全域 caches 屬性公開,因此您可以使用簡單的功能偵測來測試 API 是否存在:

const cacheAvailable = 'caches' in self;

瀏覽器支援

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

資料來源

您可以透過視窗、iframe、worker 或 Service Worker 存取 Cache API。

可儲存的內容

快取只會分別儲存代表 HTTP 要求和回應的 RequestResponse 物件組合。不過,要求和回應可以包含任何可透過 HTTP 傳輸的資料。

可以儲存多少?

簡單來說,是指「很多」,至少數百 MB,甚至可能數百 GB 以上。瀏覽器實作方式各有不同,但可用的儲存空間量通常取決於裝置的可用儲存空間量。

建立及開啟快取

如要開啟快取,請使用 caches.open(name) 方法,並將快取名稱傳遞為單一參數。如果指定的快取不存在,系統會建立快取。這個方法會傳回與 Cache 物件解析的 Promise

const cache = await caches.open('my-cache');
// do something with cache...

新增至快取

將項目新增至快取有三種方法:addaddAllput。這三種方法都會傳回 Promise

cache.add

首先,有 cache.add()。它會採用一個參數,可能是 Request 或網址 (string)。它會向網路發出要求,並將回應儲存在快取中。如果擷取失敗,或是回應的狀態碼不在 200 範圍內,則不會儲存任何內容,Promise 也會拒絕。請注意,如果跨來源要求不在 CORS 模式中,就無法儲存,因為這類要求會傳回 0status。這類要求只能儲存在 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);

在這些情況下,新項目會覆寫任何相符的現有項目。這會使用「擷取」一節所述的相同比對規則。

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 物件:

const request = new Request('/my-data-store/item-id');

使用 Response 物件

Response 物件建構函式接受多種資料,包括 BlobArrayBufferFormData 物件和字串。

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 會具有相同的類型。否則,系統會使用 ResponseContent-Type
text 將內文的位元組解讀為 UTF-8 編碼字串。
json 將主體的位元組解讀為 UTF-8 編碼字串,然後嘗試將其剖析為 JSON。傳回產生的物件,如果字串無法解析為 JSON,則擲回 TypeError
formData 將內文的位元組解讀為 HTML 表單,並以 multipart/form-dataapplication/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 方法 (GETPOSTPUT 等) 不同,系統會視為不同的要求。

您可以將選項物件做為第二個參數傳遞,藉此忽略部分或所有這些項目。

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(),這也是捷徑。

搜尋中

Cache API 不提供搜尋要求或回應的方法,但可比對 Response 物件的項目。不過,您可以使用篩選或建立索引來實作自己的搜尋。

篩選

實作自己的搜尋方法之一,就是反覆執行所有項目,並篩選出您需要的項目。假設您要找出網址結尾為 .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;
}

這樣一來,您可以使用 RequestResponse 物件的任何屬性來篩選項目。請注意,搜尋大型資料集時,這項作業的速度會十分緩慢。

建立索引

實作自訂搜尋的另一種方式,是維護可搜尋項目的索引,並將索引儲存在 IndexedDB 中。這是 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 上。