Lo spazio di archiviazione della cache è uno strumento potente. Le tue app sono meno dipendenti dalle condizioni della rete. Con un buon utilizzo delle cache, puoi rendere la tua app web disponibile offline e pubblicare gli asset il più rapidamente possibile in qualsiasi condizione di rete. Come indicato in Asset e dati, puoi decidere la strategia migliore per memorizzare nella cache gli asset necessari. Per gestire la cache, il tuo service worker interagisce con l'API Cache Storage.
L'API Cache Storage è disponibile in diversi contesti:
- Il contesto della finestra (thread principale della PWA).
- Il service worker.
- Eventuali altri worker che utilizzi.
Un vantaggio della gestione della cache tramite i worker di servizio è che il relativo ciclo di vita non è legato alla finestra, il che significa che non blocchi il thread principale. Tieni presente che per utilizzare l'API Cache Storage la maggior parte di questi contesti deve essere sotto una connessione TLS.
Che cosa memorizzare nella cache
La prima domanda che potresti porti sulla memorizzazione nella cache riguarda cosa memorizzare nella cache. Sebbene non esista una sola risposta a questa domanda, puoi iniziare con tutte le risorse minime necessarie per eseguire il rendering dell'interfaccia utente.
Queste risorse devono includere:
- Il codice HTML della pagina principale (start_url dell'app).
- Le stylesheet CSS necessarie per l'interfaccia utente principale.
- Immagini utilizzate nell'interfaccia utente.
- File JavaScript necessari per il rendering dell'interfaccia utente.
- Dati, ad esempio un file JSON, necessari per visualizzare un'esperienza di base.
- Caratteri web.
- In un'applicazione multipagina, altri documenti HTML che vuoi pubblicare rapidamente o offline.
Pronto per l'offline
Sebbene la compatibilità offline sia uno dei requisiti per un'app web progressiva, è essenziale capire che non tutte le PWA richiedono un'esperienza offline completa, ad esempio le soluzioni di cloud gaming o le app di asset di criptovaluta. Pertanto, è consentito offrire un'interfaccia utente di base che guidi gli utenti in queste situazioni.
La PWA non deve visualizzare il messaggio di errore di un browser che indica che il motore di rendering web non è riuscito a caricare la pagina. Utilizza invece il tuo service worker per mostrare i tuoi messaggi, evitando un errore del browser generico e confuso.
Esistono molte strategie di memorizzazione nella cache che puoi utilizzare a seconda delle esigenze della tua PWA. Ecco perché è importante progettare l'utilizzo della cache in modo da offrire un'esperienza rapida e affidabile. Ad esempio, se tutti gli asset dell'app vengono scaricati rapidamente, non occupano molto spazio e non devono essere aggiornati in ogni richiesta, la memorizzazione nella cache di tutti gli asset sarebbe una strategia valida. Se invece hai risorse che devono essere nella versione più recente, ti consigliamo di non memorizzare nella cache questi asset.
Utilizzo dell'API
Utilizza l'API Cache Storage per definire un insieme di cache all'interno dell'origine, ognuna identificata con un nome di stringa che puoi definire. Accedi all'API tramite l'oggetto caches
e il metodo open
consente la creazione o l'apertura di una cache già creata. Il metodo open restituisce una promessa per l'oggetto cache.
caches.open("pwa-assets")
.then(cache => {
// you can download and store, delete or update resources with cache arguments
});
Download e archiviazione di asset
Per chiedere al browser di scaricare e archiviare gli asset, utilizza i metodi add
o addAll
. Il metodo add
invia una richiesta e memorizza una risposta HTTP, mentre addAll
un gruppo di risposte HTTP come transazione in base a un array di richieste o URL.
caches.open("pwa-assets")
.then(cache => {
cache.add("styles.css"); // it stores only one resource
cache.addAll(["styles.css", "app.js"]); // it stores two resources
});
L'interfaccia di archiviazione della cache memorizza l'intera risposta, incluse tutte le intestazioni e il corpo. Di conseguenza, puoi recuperarlo in un secondo momento utilizzando una richiesta HTTP o un URL come chiave. Scopri come farlo nel capitolo Pubblicazione.
Quando memorizzare nella cache
Nella tua PWA, sei tu a decidere quando memorizzare nella cache i file. Sebbene un approccio sia quello di archiviare il maggior numero possibile di asset quando viene installato il service worker, in genere non è la soluzione migliore. La memorizzazione nella cache di risorse non necessarie comporta uno spreco di larghezza di banda e spazio di archiviazione e potrebbe causare il caricamento di risorse obsolete non intenzionali da parte dell'app.
Non è necessario memorizzare nella cache tutti gli asset contemporaneamente, ma puoi farlo più volte durante il ciclo di vita della tua PWA, ad esempio:
- All'installazione del service worker.
- Dopo il primo caricamento della pagina.
- Quando l'utente passa a una sezione o a un percorso.
- Quando la rete è inattiva.
Puoi richiedere la memorizzazione nella cache di nuovi file nel thread principale o nel contesto del service worker.
Memorizzazione nella cache degli asset in un service worker
Uno degli scenari più comuni è memorizzare nella cache un insieme minimo di asset quando viene installato il service worker. Per farlo, puoi utilizzare l'interfaccia di archiviazione della cache all'interno dell'evento install
nel service worker.
Poiché il thread del worker del servizio può essere interrotto in qualsiasi momento, puoi chiedere al browser di attendere il completamento della promessa addAll
per aumentare le possibilità di archiviare tutti gli asset e mantenere l'app coerente. L'esempio seguente mostra come eseguire questa operazione utilizzando il metodo waitUntil
dell'argomento evento ricevuto nell'ascoltatore di eventi del worker di servizio.
const urlsToCache = ["/", "app.js", "styles.css", "logo.svg"];
self.addEventListener("install", event => {
event.waitUntil(
caches.open("pwa-assets")
.then(cache => {
return cache.addAll(urlsToCache);
});
);
});
Il metodo waitUntil()
riceve una promessa e chiede al browser di attendere la risoluzione dell'attività nella promessa (soddisfatta o non riuscita) prima di terminare il processo del worker di servizio. Potresti dover concatenare le promesse e restituire le chiamate add()
o addAll()
in modo che un singolo risultato venga inviato al metodo waitUntil()
.
Puoi anche gestire le promesse utilizzando la sintassi async/await. In questo caso, devi creare una funzione asincrona che possa chiamare await
e che restituisca una promessa a waitUntil()
dopo la chiamata, come nell'esempio seguente:
const urlsToCache = ["/", "app.js", "styles.css", "logo.svg"];
self.addEventListener("install", (event) => {
let cacheUrls = async () => {
const cache = await caches.open("pwa-assets");
return cache.addAll(urlsToCache);
};
event.waitUntil(cacheUrls());
});
Richieste interdominio e risposte non trasparenti
La tua PWA può scaricare e memorizzare nella cache gli asset della tua origine e di più domini, ad esempio i contenuti di CDN di terze parti. Con un'app cross-domain, l'interazione con la cache è molto simile alle richieste con la stessa origine. La richiesta viene eseguita e una copia della risposta viene memorizzata nella cache. Come per gli altri asset memorizzati nella cache, può essere utilizzato solo nell'origine dell'app.
La risorsa verrà archiviata come risposta opaca, il che significa che il codice non potrà vedere o modificare i contenuti o le intestazioni di questa risposta. Inoltre, le risposte opache non espongono le dimensioni effettive nell'API di archiviazione, influenzando le quote. Alcuni browser mostrano dimensioni elevate, ad esempio 7 MB, indipendentemente dal fatto che il file sia di soli 1 KB.
Aggiornamento ed eliminazione di asset
Puoi aggiornare gli asset utilizzando cache.put(request, response)
ed eliminarli con delete(request)
.
Per ulteriori dettagli, consulta la documentazione dell'oggetto Cache.
Debug dello spazio di archiviazione cache
Molti browser offrono un modo per eseguire il debug dei contenuti dello spazio di archiviazione della cache nella scheda Applicazione di DevTools. Qui puoi vedere i contenuti di ogni cache all'interno dell'origine corrente. Scopriremo di più su questi strumenti nel capitolo Strumenti e debug.