Interfejs Cache API: krótki przewodnik

Dowiedz się, jak za pomocą interfejsu Cache API udostępnić dane aplikacji offline.

Cache API to system przechowywania i pobierania żądań sieciowych oraz odpowiadających im odpowiedzi. Mogą to być zwykłe żądania i odpowiedzi tworzone w trakcie uruchamiania aplikacji lub utworzone wyłącznie do przechowywania danych do późniejszego wykorzystania.

Interfejs Cache API został utworzony, aby umożliwić mechanizmom service worker zapisywanie żądań sieciowych w pamięci podręcznej. Dzięki temu mogą one wykonywać szybkie odpowiedzi niezależnie od szybkości czy dostępności sieci. Interfejsu API można jednak również używać jako ogólnego mechanizmu przechowywania danych.

Gdzie jest dostępna?

Interfejs Cache API jest dostępny we wszystkich nowoczesnych przeglądarkach. Jest udostępniana za pomocą globalnej właściwości caches, więc możesz przetestować obecność interfejsu API za pomocą prostego wykrywania funkcji:

const cacheAvailable = 'caches' in self;

Obsługa przeglądarek

  • 40
  • 16
  • 41
  • 11.1

Źródło

Dostęp do interfejsu Cache API można uzyskać z okna, elementu iframe, instancji roboczej lub skryptu service worker.

Co można przechowywać

W pamięci podręcznej przechowywane są tylko pary obiektów Request i Response, reprezentujące żądania i odpowiedzi HTTP. Żądania i odpowiedzi mogą jednak zawierać dowolne dane, które można przesyłać przez HTTP.

Ile można przechowywać?

Krótko mówiąc, dużo, co najmniej kilkaset megabajtów, a potencjalnie setki gigabajtów i więcej. Implementacje przeglądarek mogą się różnić, ale ilość dostępnego miejsca zależy zwykle od ilości miejsca dostępnego na urządzeniu.

Tworzenie i otwieranie pamięci podręcznej

Aby otworzyć pamięć podręczną, użyj metody caches.open(name), przekazując nazwę pamięci podręcznej jako pojedynczy parametr. Jeśli pamięć podręczna nie istnieje, zostanie utworzona. Ta metoda zwraca kod Promise, który zwracany jest obiektem Cache.

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

Dodawanie do pamięci podręcznej

Element można dodać do pamięci podręcznej na 3 sposoby: add, addAll i put. Wszystkie 3 metody zwracają błąd Promise.

cache.add

Pierwsza z nich to cache.add(). Przyjmuje on 1 parametr: Request lub URL (string). Wysyła żądanie do sieci i zapisuje odpowiedź w pamięci podręcznej. Jeśli pobieranie się nie uda lub kod stanu odpowiedzi nie mieści się w zakresie 200, nic nie jest zapisywane i Promise odrzuca. Pamiętaj, że nie można przechowywać żądań z innych domen, które nie są w trybie CORS, ponieważ zwracają wartość status o wartości 0. Takie żądania można przechowywać tylko w usłudze 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

Kolejny krok to cache.addAll(). Działa podobnie do add(), ale przyjmuje tablicę obiektów lub adresów URL Request (string). Działa to podobnie do wywoływania cache.add dla każdego indywidualnego żądania z tą różnicą, że Promise odrzuca, jeśli którekolwiek żądanie nie jest przechowywane w pamięci podręcznej.

const urls = ['/weather/today.json', '/weather/tomorrow.json'];
cache.addAll(urls);

W każdym z tych przypadków nowy wpis zastąpi każdy zgodny z istniejącym wpisem. Są wtedy używane te same reguły dopasowania, które są opisane w sekcji poświęconej retrieving.

cache.put

Na koniec jest jeszcze cache.put(), który pozwala zapisać odpowiedź z sieci lub utworzyć i zapisać własny Response. Potrzebne są 2 parametry. Pierwszym z nich może być obiekt Request lub adres URL (string). Drugi to obiekt Response z sieci lub wygenerowany przez Twój kod.

// 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');

Metoda put() jest mniej restrykcyjna niż metoda add() lub addAll() i umożliwia przechowywanie odpowiedzi innych niż CORS lub innych odpowiedzi, w których kod stanu odpowiedzi nie mieści się w zakresie 200. Spowoduje to zastąpienie poprzednich odpowiedzi na to samo żądanie.

Tworzenie obiektów żądań

Utwórz obiekt Request, używając adresu URL przechowywanej rzeczy:

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

Praca z obiektami odpowiedzi

Konstruktor obiektów Response akceptuje wiele typów danych, w tym obiekty Blob, ArrayBuffer, obiekty FormData i ciągi znaków.

const imageBlob = new Blob([data], {type: 'image/jpeg'});
const imageResponse = new Response(imageBlob);
const stringResponse = new Response('Hello world');

Możesz ustawić typ MIME elementu Response, ustawiając odpowiedni nagłówek.

  const options = {
    headers: {
      'Content-Type': 'application/json'
    }
  }
  const jsonResponse = new Response('{}', options);

Jeśli po pobraniu elementu Response chcesz uzyskać dostęp do jego treści, możesz skorzystać z kilku metod pomocniczych. Każdy zwraca obiekt Promise, który kończy się wartością innego typu.

Metoda Opis
arrayBuffer Zwraca ArrayBuffer zawierający treść zserializowaną do bajtów.
blob Zwraca wartość Blob. Jeśli Response został utworzony z Blob, nowy Blob będzie tego samego typu. W przeciwnym razie używany jest Content-Type z Response.
text Interpretuje bajty treści jako ciąg zakodowany w formacie UTF-8.
json Interpretuje bajty treści jako ciąg zakodowany w formacie UTF-8, a następnie próbuje przeanalizować go jako JSON. Zwraca wynikowy obiekt lub zwraca TypeError, jeśli ciągu nie można przeanalizować jako JSON.
formData Interpretuje bajty treści jako formę HTML zakodowaną jako multipart/form-data lub application/x-www-form-urlencoded. Zwraca obiekt FormData lub zwraca TypeError, jeśli danych nie można przeanalizować.
body Zwraca wartość ReadableStream dla danych treści.

Przykład

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]

Pobieram z pamięci podręcznej

Aby znaleźć element w pamięci podręcznej, możesz użyć metody match.

const response = await cache.match(request);
console.log(request, response);

Jeśli request jest ciągiem znaków, przeglądarka przekształca go na Request, wywołując new Request(request). Funkcja zwraca wartość Promise, która przyjmuje wartość Response, jeśli znaleziono pasujący wpis, lub w przeciwnym razie zwraca wartość undefined.

Aby ustalić, czy pasują do 2 elementów Requests, przeglądarka używa nie tylko adresu URL. 2 żądania są uznawane za różne, jeśli mają różne ciągi zapytań, nagłówki Vary lub metody HTTP (GET, POST, PUT itd.).

Możesz zignorować niektóre lub wszystkie z nich, przekazując obiekt „options” jako drugi parametr.

const options = {
  ignoreSearch: true,
  ignoreMethod: true,
  ignoreVary: true
};

const response = await cache.match(request, options);
// do something with the response

Jeśli pasuje więcej niż 1 żądanie z pamięci podręcznej, zwracane jest to, które zostało utworzone jako pierwsze. Jeśli chcesz pobrać wszystkie pasujące odpowiedzi, możesz użyć polecenia 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.`);

Jako skrót możesz przeszukać wszystkie pamięci podręczne jednocześnie, używając polecenia caches.match(), zamiast wywoływać metodę cache.match() dla każdej pamięci podręcznej.

Szukam

Interfejs Cache API nie umożliwia wyszukiwania żądań ani odpowiedzi z wyjątkiem dopasowywania wpisów do obiektu Response. Możesz jednak zaimplementować własne wyszukiwanie, używając filtrów lub tworząc indeks.

Filtrowanie

Jednym ze sposobów na wdrożenie własnego wyszukiwania jest iteracja wszystkich wpisów i odfiltrowanie tylko tych, które Cię interesują. Załóżmy, że chcesz znaleźć wszystkie elementy, których adresy URL kończą się ciągiem .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;
}

Dzięki temu do filtrowania wpisów możesz używać dowolnej właściwości obiektów Request i Response. Pamiętaj, że w przypadku wyszukiwania w dużych zbiorach danych działa to wolno.

Tworzenie indeksu

Innym sposobem na wdrożenie własnego wyszukiwania jest utrzymanie osobnego indeksu wpisów, które można przeszukiwać, i zapisanie go w IndexedDB. Ponieważ do tego rodzaju operacji zaprojektowano IndexedDB, ma ona znacznie większą wydajność przy dużej liczbie wpisów.

Jeśli przechowujesz adres URL Request obok właściwości dostępnych do wyszukiwania, po przeprowadzeniu wyszukiwania możesz łatwo odzyskać prawidłowy wpis w pamięci podręcznej.

Usuwanie elementu

Aby usunąć element z pamięci podręcznej:

cache.delete(request);

Gdzie żądanie może być ciągiem Request lub adresem URL. Ta metoda wykorzystuje też ten sam obiekt opcji co cache.match, co pozwala usunąć wiele par Request/Response dla tego samego adresu URL.

cache.delete('/example/file.txt', {ignoreVary: true, ignoreSearch: true});

Usuwanie pamięci podręcznej

Aby usunąć pamięć podręczną, wywołaj caches.delete(name). Ta funkcja zwraca wartość Promise, która przyjmuje wartość true, jeśli pamięć podręczna istniała i została usunięta, lub false w innym przypadku.

Dziękujemy

Dzięki firmie Mat Scales, która napisała oryginalną wersję tego artykułu, który po raz pierwszy ukazał się w WebFundamentals.