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ł stworzony, aby umożliwić usługom workera zapisywanie w pamięci podręcznej żądań sieciowych, dzięki czemu mogą one szybko odpowiadać niezależnie od szybkości sieci lub 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 dostępny za pomocą właściwości globalnej caches, dzięki czemu możesz sprawdzić 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

Do interfejsu Cache API można uzyskać dostęp z poziomu okna, elementu iframe, skryptu worker 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, addAll i put. Wszystkie 3 metody zwracają Promise.

cache.add

Po pierwsze, cache.add(). Przyjmuje 1 parametr: Request lub URL (string). Wysyła żądanie do sieci i przechowuje odpowiedź w pamięci podręcznej. Jeśli pobieranie się nie powiedzie lub kod stanu odpowiedzi nie będzie się mieścił w zakresie 200, nic nie zostanie zapisane i funkcja Promise zostanie odrzucona. 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 cache.add w przypadku każdego żądania, z tą różnicą, że 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ępuje wszystkie pasujące wpisy. 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().Response Wymaga podania 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, używając adresu URL rzeczy, która ma być przechowywana:

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

Praca z obiektami odpowiedzi

Konstruktor obiektu Response akceptuje wiele typów danych, w tym Blobs, ArrayBuffers, 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');

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 bajtach.
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 treści jako ciąg znaków zakodowany w UTF-8.
json Interpretuje bajty treści jako ciąg znaków zakodowany w formacie UTF-8, a następnie próbuje przeanalizować go jako dane w formacie 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ą uważane za różne, jeśli mają różne ciągi znaków zapytania, nagłówki Vary lub metody HTTP (GET, POST, PUT itp.).

Możesz zignorować niektóre z tych elementów lub wszystkie, przekazując obiekt opcji 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 dotyczy 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 zaimplementować 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 wyfiltrowanie 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 na wdrożenie własnego wyszukiwania jest prowadzenie osobnego indeksu wpisów, które można wyszukiwać, i przechowywanie tego indeksu w IndexedDB. Ponieważ IndexedDB został zaprojektowany do tego typu operacji, 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.