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;
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.