Interfejs Cache API: krótki przewodnik

Dowiedz się, jak używać interfejsu Cache API, aby udostępniać dane aplikacji w trybie offline.

Interfejs Cache API to system do przechowywania i pobierania żądań sieciowych oraz odpowiadających im odpowiedzi. Mogą to być zwykłe żądania i odpowiedzi utworzone podczas działania aplikacji lub utworzone wyłącznie w celu przechowywania danych na potrzeby późniejszego wykorzystania.

Interfejs Cache API został utworzony, aby umożliwić mechanizmom Service Worker buforowanie żądań sieciowych, co pozwala na szybkie udzielanie odpowiedzi niezależnie od szybkości sieci i dostępności. Interfejsu API można jednak używać też jako ogólnego mechanizmu przechowywania.

Gdzie jest dostępna?

Interfejs Cache API jest dostępny we wszystkich nowoczesnych przeglądarkach. Jest on ujawniany 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

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

Źródło

Interfejs Cache API jest dostępny z poziomu okna, elementu iframe, instancji roboczej lub skryptu service worker.

Co można przechowywać

Pamięci podręczne przechowują tylko pary obiektów RequestResponse, które odpowiadają odpowiednio żądaniom i odpowiedziom HTTP. Żądania i odpowiedzi mogą jednak zawierać dowolne dane, które można przesłać przez HTTP.

Ile można przechowywać?

Krótko mówiąc, dużo, co najmniej kilkaset megabajtów, a potencjalnie nawet setki gigabajtów. Implementacje przeglądarek różnią się od siebie, ale ilość dostępnego miejsca na dane zależy zwykle od ilości miejsca dostępnej 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 jedyny parametr. Jeśli nazwana pamięć podręczna nie istnieje, zostanie utworzona. Ta metoda zwraca obiekt Promise, który jest rozwiązywany w obiekcie Cache.

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

Dodawanie do pamięci podręcznej

Dodanie elementu do pamięci podręcznej jest możliwe na 3 sposoby: add, addAllput. Wszystkie 3 metody zwracają Promise.

cache.add

Po pierwsze, cache.add(). Przyjmuje jeden parametr: Request lub adres URL (string). Wysyła żądanie do sieci i zapisuje odpowiedź w pamięci podręcznej. Jeśli pobieranie nie powiedzie się lub jeśli kod stanu odpowiedzi nie mieści się w zakresie 200, nic nie jest zapisywane, a Promise odrzuca. Pamiętaj, że żądań między domenami, które nie są w trybie CORS, nie można przechowywać, ponieważ zwracają one status0. Takie prośby można przechowywać tylko w 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

Następnie cache.addAll(). Działa podobnie jak add(), ale przyjmuje tablicę obiektów Request lub adresów URL (string). Działa to podobnie do wywołania funkcji cache.add w przypadku każdego pojedynczego żądania, z tym wyjątkiem, że funkcja Promise odrzuca żądanie, jeśli pojedyncze żą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 dowolny pasujący istniejący wpis. W tym celu stosuje się te same reguły dopasowywania, które są opisane w sekcji dotyczącej pobierania.

cache.put

Ostatnim typem jest cache.put(), który umożliwia przechowywanie odpowiedzi z sieci lub tworzenie i przechowywanie własnych cache.put(). Potrzeba 2 parametrów. Pierwszy może być obiektem Request lub adresem URL (string). Drugi musi być Response, pochodzący 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 bardziej liberalna niż add() lub addAll() i umożliwi Ci przechowywanie odpowiedzi niebędących odpowiedziami CORS lub innych odpowiedzi, których kod stanu nie mieści się w zakresie 200. Spowoduje to zastąpienie wszystkich wcześniejszych odpowiedzi na to samo żądanie.

Tworzenie obiektów żądań

Utwórz obiekt Request, korzystając z 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, FormData i ciągi tekstowe.

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

Typ MIME Response możesz ustawić, ustawiając odpowiedni nagłówek.

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

Jeśli masz zwrócony obiekt Response i chcesz uzyskać dostęp do jego treści, możesz użyć kilku metod pomocniczych. Każdy zwraca Promise, który zwraca wartość o innym typie.

Metoda Opis
arrayBuffer Zwraca obiekt ArrayBuffer zawierający treść sformatowaną w postaci bajtów.
blob Zwraca wartość Blob. Jeśli Response zostało utworzone za pomocą Blob, nowe Blob będzie miało ten sam typ. W przeciwnym razie używana jest wartość Content-Type atrybutu Response.
text Interpretuje bajty w polu tekstowym jako ciąg znaków zakodowany w 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 obiekt wynikowy lub TypeError, jeśli nie można przetworzyć ciągu jako JSON.
formData Interpretuje bajty w treści jako formularz HTML, zakodowany jako multipart/form-data lub application/x-www-form-urlencoded. Zwraca obiekt FormData lub TypeError, jeśli nie można przeanalizować danych.
body Zwraca ReadableStream dla danych zawartych w 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]

Pobieranie 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 to ciąg znaków, przeglądarka przekształca go w Request, wywołując funkcję new Request(request). Funkcja zwraca wartość Promise, która jest interpretowana jako Response, jeśli zostanie znaleziony pasujący wpis, lub undefined w przeciwnym razie.

Aby określić, czy 2 wartości Requests są takie same, przeglądarka korzysta z czegoś więcej niż tylko adresu URL. Dwa żądania są uznawane za różne, jeśli mają różne ciągi zapytań, nagłówki Vary lub metody HTTP (GET, POST, PUT itp.).

Możesz zignorować niektóre lub wszystkie z tych elementów, 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 dopasowanie występuje w przypadku więcej niż 1 żądania w 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ć elementu 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 naraz, używając funkcji caches.match()zamiast wywoływać funkcję cache.match() dla każdej pamięci podręcznej.

Szukam

Interfejs Cache API nie umożliwia wyszukiwania żądań ani odpowiedzi, z wyjątkiem dopasowania wpisów do obiektu Response. Możesz jednak wdrożyć własne wyszukiwanie, stosując filtrowanie lub tworząc indeks.

Filtrowanie

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

W ten sposób możesz użyć dowolnej właściwości obiektów Request i Response, aby filtrować wpisy. Pamiętaj, że w przypadku wyszukiwania dużych zbiorów danych proces może być powolny.

Tworzenie indeksu

Innym sposobem implementacji własnego wyszukiwania jest utrzymanie osobnego indeksu wpisów, które można przeszukiwać i przechowywać w IndexedDB. IndexedDB został zaprojektowany z myślą o takim rodzaju operacjach, dlatego jego wydajność jest znacznie lepsza w przypadku dużej liczby wpisów.

Jeśli przechowujesz adres URL Request obok właściwości wyszukiwalnych, możesz łatwo pobrać odpowiedni wpis w pamięci podręcznej po przeprowadzeniu wyszukiwania.

Usuwanie elementu

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

cache.delete(request);

Gdzie request może być Request lub ciągiem znaków adresu URL. Ta metoda przyjmuje ten sam obiekt opcji co cache.match, co pozwala usuwać 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 funkcję caches.delete(name). Ta funkcja zwraca wartość Promise, która jest równa true, jeśli pamięć podręczna istniała i została usunięta, lub false w przeciwnym razie.

Dziękujemy

Dziękujemy Matowi Scalesowi, który napisał pierwotną wersję tego artykułu, który po raz pierwszy pojawił się na stronie WebFundamentals.