L'API Cache: guida rapida

Scopri come utilizzare l'API Cache per rendere disponibili offline i dati della tua applicazione.

L'API Cache è un sistema per l'archiviazione e il recupero delle richieste di rete e delle relative risposte. Queste possono essere richieste e risposte regolari create durante l'esecuzione dell'applicazione o potrebbero essere create esclusivamente al fine di archiviare i dati per utilizzarli in un secondo momento.

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 generale di archiviazione.

Dove è disponibile?

L'API Cache è disponibile in tutti i browser moderni. Viene esposto tramite la proprietà caches globale, quindi puoi testare la presenza dell'API con un semplice rilevamento delle funzionalità:

const cacheAvailable = 'caches' in self;

Supporto dei browser

  • 40
  • 16
  • 41
  • 11.1

Origine

È possibile accedere all'API Cache da una finestra, un iframe, un worker o un service worker.

Elementi archiviabili

Le cache archiviano solo coppie di oggetti Request e Response, che rappresentano rispettivamente richieste e risposte HTTP. Tuttavia, le richieste e le risposte possono contenere qualsiasi tipo di dati che può essere trasferito tramite HTTP.

Quanti dati si possono memorizzare?

In breve, molti, almeno un paio di centinaia di megabyte e potenzialmente centinaia di gigabyte o più. Le implementazioni dei browser variano, ma in genere la quantità di spazio di archiviazione disponibile dipende dalla 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 in corso...

Esistono tre modi per aggiungere un elemento a una cache: add, addAll e put. Tutti e tre i metodi restituiscono un valore Promise.

cache.add

Innanzitutto, le sono cache.add(). Richiede un parametro, Request o URL (string), invia una richiesta alla rete e archivia la risposta nella cache. Se il recupero non va a buon fine o se il codice di stato della risposta non è compreso nell'intervallo dei 200, non viene archiviato nulla e Promise viene rifiutato. Tieni presente che le richieste multiorigine che non sono in modalità CORS non possono essere archiviate perché restituiscono un valore status 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 Request oggetti o URL (string s). Funziona in modo simile alla chiamata di cache.add per ogni singola richiesta, ad eccezione del fatto che Promise viene rifiutato se una singola richiesta non viene memorizzata nella cache.

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

In ognuno di questi casi, una nuova voce sovrascrive qualsiasi voce esistente corrispondente. Vengono utilizzate le stesse regole di corrispondenza descritte nella sezione sul retrieving.

cache.put

Infine, c'è cache.put(), che ti consente di archiviare una risposta della rete o di creare e archiviare il tuo Response. Sono necessari due parametri. Il primo può essere un oggetto Request o un URL (string). Il secondo deve essere un Response, proveniente dalla 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 archiviare risposte non CORS o altre risposte il cui codice di stato della risposta non è compreso nell'intervallo 200. Le risposte precedenti per la stessa richiesta verranno sovrascritte.

Creazione di oggetti di richiesta

Crea l'oggetto Request utilizzando un URL per l'elemento da archiviare:

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

Utilizzo degli oggetti Response

Il costruttore di oggetti Response accetta molti tipi di dati, tra cui oggetti Blob, ArrayBuffer, 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 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, puoi usare diversi metodi di supporto. Ciascuno restituisce un Promise che si risolve con un valore di un tipo diverso.

Metodo Descrizione
arrayBuffer Restituisce un elemento ArrayBuffer contenente il corpo, serializzato in byte.
blob Restituisce un Blob. Se Response è stato creato con un Blob, questo nuovo Blob ha lo stesso tipo. In caso contrario, viene utilizzato Content-Type di Response.
text Interpreta i byte del corpo come stringa con codifica UTF-8.
json Interpreta i byte del corpo come stringa codificata in 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 o genera un TypeError se i dati non possono essere analizzati.
body Restituisce un valore 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 usare il metodo match.

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

Se request è una stringa, il browser la converte in Request richiamando new Request(request). La funzione restituisce un Promise che si risolve in un Response se viene trovata una voce corrispondente o in undefined nel caso contrario.

Per determinare se due Requests corrispondono, il browser non si limita a utilizzare l'URL. Due richieste sono considerate diverse se hanno stringhe di query, intestazioni Vary o metodi HTTP diversi (GET, POST, PUT e così via).

Puoi ignorare alcuni o tutti questi aspetti 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, quella creata per prima viene restituita. 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() anziché chiamare cache.match() per ogni cache.

Ricerca in corso

L'API Cache non fornisce un modo per cercare richieste o risposte, ad eccezione delle voci corrispondenti a un oggetto Response. Tuttavia, puoi implementare la tua ricerca utilizzando i filtri o creando un indice.

Applicazione dei filtri

Un modo per implementare la tua ricerca è eseguire l'iterazione di tutte le voci e filtrare quelle desiderate. Supponiamo di voler trovare tutti gli elementi che hanno 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 per filtrare le voci. Tieni presente che l'operazione è lenta se cerchi grandi set di dati.

Creazione di un indice

L'altro modo per implementare la tua ricerca è mantenere un indice separato di voci in cui è possibile eseguire ricerche e archiviare l'indice in IndexedDB. Poiché questo è il tipo di operazione per cui è stato progettato IndexedDB, offre prestazioni molto migliori con un numero elevato di voci.

Se archivi l'URL dell'Request insieme alle proprietà disponibili per la ricerca, puoi recuperare facilmente la voce corretta della cache 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 utilizza 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});

Eliminazione di una cache

Per svuotare una cache, chiama caches.delete(name). Questa funzione restituisce un Promise che si risolve in true se la cache esisteva ed è stata eliminata oppure false in caso contrario.

Grazie

Grazie a Mat Scales, che ha scritto la versione originale di questo articolo, apparso per la prima volta su WebFundamentals.