Scopri come utilizzare l'API Cache per rendere disponibili i dati dell'applicazione offline.
L'API Cache è un sistema per archiviare e recuperare le richieste di rete e le relative risposte. Potrebbero trattarsi di richieste e risposte regolari create durante l'esecuzione dell'applicazione oppure essere create esclusivamente allo scopo di archiviare i dati per un uso successivo.
L'API Cache è stata creata per consentire ai service worker di memorizzare nella cache le richieste di rete in modo da poter fornire risposte rapide, indipendentemente dalla velocità o dalla disponibilità della rete. Tuttavia, l'API può essere utilizzata anche come meccanismo di archiviazione generale.
Dove è disponibile?
L'API Cache è disponibile in tutti i browser moderni. È esposta tramite la proprietà caches
globale, quindi puoi verificare la presenza dell'API con un semplice rilevamento delle funzionalità:
const cacheAvailable = 'caches' in self;
È possibile accedere all'API Cache da una finestra, un iframe, un worker o un service worker.
Che cosa è possibile memorizzare
Le cache memorizzano solo coppie di oggetti Request
e
Response
, che rappresentano rispettivamente le richieste e le risposte HTTP. Tuttavia, le richieste e le risposte possono contenere qualsiasi tipo di dato
che può essere trasferito tramite HTTP.
Quanto è possibile memorizzare?
In breve, molto, almeno un paio di centinaia di megabyte e potenzialmente anche centinaia di gigabyte o più. Le implementazioni dei browser variano, ma la quantità di spazio di archiviazione disponibile si basa in genere sulla quantità di spazio di archiviazione disponibile sul dispositivo.
Creazione e apertura di una cache
Per aprire una cache, utilizza il metodo caches.open(name)
, passando il nome della cache come singolo parametro. Se la cache denominata non esiste, viene creata. Questo metodo restituisce un Promise
che si risolve con l'oggetto Cache
.
const cache = await caches.open('my-cache');
// do something with cache...
Aggiunta a una cache
Esistono tre modi per aggiungere un elemento a una cache: add
, addAll
e put
.
Tutti e tre i metodi restituiscono un Promise
.
cache.add
Innanzitutto, c'è cache.add()
. Richiede un parametro, Request
o un URL (string
). Invia una richiesta alla rete e memorizza la risposta nella cache. Se il recupero non va a buon fine o se il codice di stato della risposta non rientra nell'intervallo 200, non viene memorizzato nulla e Promise
viene rifiutato. Tieni presente che le richieste cross-origin non in modalità CORS non possono essere memorizzate perché restituiscono un status
di 0
. Queste richieste possono essere archiviate solo con 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
Poi c'è cache.addAll()
. Funziona in modo simile a add()
, ma richiede un array di oggetti o URL Request
(string
). Il funzionamento è simile alla chiamata di cache.add
per ogni singola richiesta, tranne per il fatto che Promise
rifiuta se una singola richiesta non viene memorizzata nella cache.
const urls = ['/weather/today.json', '/weather/tomorrow.json'];
cache.addAll(urls);
In ciascuno di questi casi, una nuova voce sovrascrive qualsiasi voce esistente corrispondente. Vengono utilizzate le stesse regole di corrispondenza descritte nella sezione sul recupero.
cache.put
Infine, c'è cache.put()
, che ti consente di memorizzare una risposta
della rete o di creare e memorizzare il tuo cache.put()
.Response
Richiede due parametri. Il primo può essere un oggetto Request
o un URL (string
).
Il secondo deve essere un Response
, della rete o generato dal tuo
codice.
// 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');
Il metodo put()
è più permissivo di add()
o addAll()
e consente di memorizzare risposte non CORS o altre risposte in cui il codice di stato della risposta non rientra nell'intervallo 200. Sostituirà eventuali risposte precedenti per la stessa richiesta.
Creazione di oggetti Request
Crea l'oggetto Request
utilizzando un URL per la cosa da archiviare:
const request = new Request('/my-data-store/item-id');
Utilizzo degli oggetti Response
Il costruttore dell'oggetto Response
accetta molti tipi di dati, tra cui Blob
, ArrayBuffer
, oggetti FormData
e stringhe.
const imageBlob = new Blob([data], {type: 'image/jpeg'});
const imageResponse = new Response(imageBlob);
const stringResponse = new Response('Hello world');
Puoi impostare il tipo MIME di un Response
impostando l'intestazione appropriata.
const options = {
headers: {
'Content-Type': 'application/json'
}
}
const jsonResponse = new Response('{}', options);
Se hai recuperato un Response
e vuoi accedere al relativo corpo, esistono diversi metodi di assistenza che puoi utilizzare. Ognuno restituisce un Promise
che si risolve con un valore di tipo diverso.
Metodo | Descrizione |
---|---|
arrayBuffer |
Restituisce un ArrayBuffer contenente il corpo, serializzato in byte.
|
blob |
Restituisce un Blob . Se il Response è stato creato
con un Blob , questo nuovo Blob ha lo stesso
tipo. In caso contrario, viene utilizzato il Content-Type del
Response .
|
text |
Interpreta i byte del corpo come stringa codificata UTF-8. |
json |
Interpreta i byte del corpo come stringa codificata UTF-8, quindi tenta di analizzarla come JSON. Restituisce l'oggetto risultante o genera un TypeError se la stringa non può essere analizzata come JSON.
|
formData |
Interpreta i byte del corpo come un modulo HTML, codificato come
multipart/form-data o
application/x-www-form-urlencoded . Restituisce un oggetto
FormData
oppure genera un TypeError se non è possibile analizzare i dati.
|
body |
Restituisce un ReadableStream per i dati del corpo. |
Ad esempio:
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]
Recupero da una cache
Per trovare un elemento in una cache, puoi utilizzare il metodo match
.
const response = await cache.match(request);
console.log(request, response);
Se request
è una stringa, il browser la converte in Request
chiamando
new Request(request)
. La funzione restituisce un Promise
che si risolve in un Response
se viene trovata una voce corrispondente o in un undefined
in caso contrario.
Per determinare se due Requests
corrispondono, il browser utilizza più dell'URL. Due richieste sono considerate diverse se hanno stringhe di query, intestazioni Vary
o metodi HTTP (GET
, POST
, PUT
e così via) diversi.
Puoi ignorare alcune o tutte queste opzioni passando un oggetto options come secondo parametro.
const options = {
ignoreSearch: true,
ignoreMethod: true,
ignoreVary: true
};
const response = await cache.match(request, options);
// do something with the response
Se più di una richiesta memorizzata nella cache corrisponde, viene restituita quella creata per prima. Se vuoi recuperare tutte le risposte corrispondenti, puoi utilizzare
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.`);
Come scorciatoia, puoi cercare in tutte le cache contemporaneamente utilizzando caches.match()
invece di chiamare cache.match()
per ogni cache.
Ricerca in corso
L'API Cache non fornisce un modo per cercare richieste o risposte
tranne che per le voci corrispondenti a un oggetto Response
. Tuttavia, puoi implementare la tua ricerca utilizzando i filtri o creando un indice.
Filtri
Un modo per implementare la tua ricerca è eseguire l'iterazione su tutte le voci e limitare il risultato a quelle che ti interessano. Supponiamo che tu voglia trovare tutti gli elementi con URL che terminano con .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;
}
In questo modo puoi utilizzare qualsiasi proprietà degli oggetti Request
e Response
perfiltrare le voci. Tieni presente che questa operazione è lenta se esegui ricerche in set di dati di grandi dimensioni.
Creazione di un indice
L'altro modo per implementare la tua ricerca è mantenere un indice distinto delle voci che possono essere cercate e archiviarlo in IndexedDB. Poiché questo è il tipo di operazione per cui è stato progettato IndexedDB, ha prestazioni molto migliori con gran numero di voci.
Se memorizzi l'URL del Request
insieme alle proprietà che possono essere cercate, puoi recuperare facilmente la voce della cache corretta dopo aver eseguito la ricerca.
Eliminazione di un elemento
Per eliminare un elemento da una cache:
cache.delete(request);
Dove la richiesta può essere un Request
o una stringa URL. Questo metodo accetta anche lo stesso oggetto opzioni di cache.match
, che consente di eliminare più coppie Request
/Response
per lo stesso URL.
cache.delete('/example/file.txt', {ignoreVary: true, ignoreSearch: true});
Eliminare una cache
Per eliminare una cache, chiama caches.delete(name)
. Questa funzione restituisce un valore Promise
che si risolve in true
se la cache esisteva ed è stata eliminata o in false
in caso contrario.
Grazie
Grazie a Mat Scales che ha scritto la versione originale di questo articolo, che è stata pubblicata per la prima volta su WebFundamentals.