Un aspetto fondamentale delle app web progressive è la loro affidabilità: possono caricare rapidamente gli asset, mantenere gli utenti coinvolti e fornire feedback immediato, anche in condizioni di rete scarse. Com'è possibile? Grazie all'evento fetch
del service worker.
L'evento di recupero
L'evento fetch
ci consente di intercettare ogni richiesta di rete effettuata dalla PWA nell'ambito del service worker, sia per le richieste con stessa origine sia per quelle multiorigine. Oltre alle richieste di navigazione e asset, il recupero da un service worker installato consente di visualizzare le visite alle pagine dopo il primo caricamento di un sito senza chiamate di rete.
Il gestore fetch
riceve tutte le richieste da un'app, inclusi URL e intestazioni HTTP, e consente allo sviluppatore dell'app di decidere come elaborarle.
Il service worker può inoltrare una richiesta alla rete, rispondere con una risposta memorizzata nella cache in precedenza o creare una nuova risposta. A te la scelta. Ecco un semplice esempio:
self.addEventListener("fetch", event => {
console.log(`URL requested: ${event.request.url}`);
});
Rispondere a una richiesta
Quando una richiesta arriva al service worker, puoi ignorarla, in modo che venga inviata alla rete, oppure puoi rispondere. Rispondere alle richieste dal service worker ti consente di scegliere cosa e come restituire i dati alla tua PWA, anche quando l'utente è offline.
Per rispondere a una richiesta in arrivo, chiama event.respondWith()
dall'interno di un gestore di eventi fetch
, in questo modo:
// fetch event handler in your service worker file
self.addEventListener("fetch", event => {
const response = .... // a response or a Promise of response
event.respondWith(response);
});
Devi chiamare respondWith()
in modo sincrono e restituire un oggetto Response. Tuttavia, non puoi chiamare respondWith()
dopo che il gestore di eventi di recupero è terminato, ad esempio all'interno di una chiamata asincrona. Se devi attendere la risposta completa, puoi passare una promessa a respondWith()
che si risolve con una risposta.
Creazione di risposte
Grazie all'API Fetch, puoi creare risposte HTTP nel tuo codice JavaScript e queste risposte possono essere memorizzate nella cache utilizzando l'API Cache Storage e restituite come se provenissero da un server web.
Per creare una risposta, crea un nuovo oggetto Response
, impostando il corpo e le opzioni, ad esempio lo stato e le intestazioni:
const simpleResponse = new Response("Body of the HTTP response");
const options = {
status: 200,
headers: {
'Content-type': 'text/html'
}
};
const htmlResponse = new Response("<b>HTML</b> content", options)
Risposta dalla cache
Ora che sai come pubblicare risposte HTTP da un service worker, è il momento di utilizzare l'interfaccia Caching Storage per archiviare gli asset sul dispositivo.
Puoi utilizzare l'API Cache Storage per verificare se la richiesta ricevuta dalla PWA è disponibile nella cache e, in caso affermativo, rispondere a respondWith()
con la richiesta.
Per farlo, devi prima eseguire una ricerca all'interno della cache. La funzione match()
, disponibile nell'interfaccia caches
di primo livello, esegue la ricerca in tutti i negozi dell'origine o in un singolo oggetto cache aperto.
La funzione match()
riceve una richiesta HTTP o un URL come argomento e restituisce una promessa che viene risolta con la risposta associata alla chiave corrispondente.
// Global search on all caches in the current origin
caches.match(urlOrRequest).then(response => {
console.log(response ? response : "It's not in the cache");
});
// Cache-specific search
caches.open("pwa-assets").then(cache => {
cache.match(urlOrRequest).then(response => {
console.log(response ? response : "It's not in the cache");
});
});
Strategie di memorizzazione nella cache
La pubblicazione di file solo dalla cache del browser non è adatta a tutti i casi d'uso. Ad esempio, l'utente o il browser possono eliminare la cache. Per questo motivo, devi definire le tue strategie per la pubblicazione degli asset per la tua PWA.
Non sei limitato a una sola strategia di memorizzazione nella cache. Puoi definirne di diversi per pattern URL diversi. Ad esempio, puoi avere una strategia per gli asset UI minimi, una per le chiamate API e una terza per gli URL di immagini e dati.
Per farlo, leggi event.request.url
in ServiceWorkerGlobalScope.onfetch
e analizzalo tramite espressioni regolari o un pattern URL. Al momento della stesura, il pattern URL non è supportato su tutte le piattaforme.
Le strategie più comuni sono:
- Cache First
- Cerca prima una risposta memorizzata nella cache e, se non la trova, torna alla rete.
- Prima rete
- Richiede prima una risposta dalla rete e, se non viene restituita alcuna risposta, controlla la cache.
- Stale While Revalidate
- Fornisce una risposta dalla cache, mentre in background richiede l'ultima versione e la salva nella cache per la volta successiva in cui viene richiesto l'asset.
- Solo emittente
- Risponde sempre con una risposta dalla rete o con errori. La cache non viene mai consultata.
- Solo cache
- Risponde sempre con una risposta dalla cache o con errori. La rete non verrà mai consultata. Gli asset che verranno pubblicati utilizzando questa strategia devono essere aggiunti alla cache prima di essere richiesti.
Cache first
Utilizzando questa strategia, il service worker cerca la richiesta corrispondente nella cache e restituisce la risposta corrispondente se è memorizzata nella cache. In caso contrario, recupera la risposta dalla rete (aggiornando facoltativamente la cache per le chiamate future). Se non è presente una risposta della cache né una risposta di rete, la richiesta genererà un errore. Poiché la pubblicazione degli asset senza passare alla rete tende a essere più veloce, questa strategia dà la priorità al rendimento rispetto alla freschezza.
self.addEventListener("fetch", event => {
event.respondWith(
caches.match(event.request)
.then(cachedResponse => {
// It can update the cache to serve updated content on the next request
return cachedResponse || fetch(event.request);
}
)
)
});
Network first
Questa strategia è l'opposto della strategia Cache First: verifica se la richiesta può essere soddisfatta dalla rete e, in caso contrario, tenta di recuperarla dalla cache. Come la cache first. Se non è presente una risposta di rete né una risposta della cache, la richiesta genererà un errore. Ottenere la risposta dalla rete è in genere più lento rispetto a ottenerla dalla cache. Questa strategia dà la priorità ai contenuti aggiornati anziché alle prestazioni.
self.addEventListener("fetch", event => {
event.respondWith(
fetch(event.request)
.catch(error => {
return caches.match(event.request) ;
})
);
});
Inattivo durante la riconvalida
La strategia Stale-While-Revalidate restituisce immediatamente una risposta memorizzata nella cache, quindi controlla la rete per un aggiornamento, sostituendo la risposta memorizzata nella cache se ne viene trovata una. Questa strategia esegue sempre una richiesta di rete, perché anche se viene trovata una risorsa memorizzata nella cache, tenta di aggiornare i contenuti della cache con quelli ricevuti dalla rete, per utilizzare la versione aggiornata nella richiesta successiva. Questa strategia, pertanto, ti consente di sfruttare i vantaggi della strategia di pubblicazione rapida della cache e di aggiornare la cache in background.
self.addEventListener('fetch', event => {
event.respondWith(
caches.match(event.request).then(cachedResponse => {
const networkFetch = fetch(event.request).then(response => {
// update the cache with a clone of the network response
const responseClone = response.clone()
caches.open(url.searchParams.get('name')).then(cache => {
cache.put(event.request, responseClone)
})
return response
}).catch(function (reason) {
console.error('ServiceWorker fetch failed: ', reason)
})
// prioritize cached response over network
return cachedResponse || networkFetch
}
)
)
})
Solo rete
La strategia solo rete è simile al comportamento dei browser senza un service worker o l'API Cache Storage. Le richieste restituiranno una risorsa solo se può essere recuperata dalla rete. Questo è spesso utile per risorse come le richieste API solo online.
Solo cache
La strategia solo cache garantisce che le richieste non vengano mai inviate alla rete. A tutte le richieste in entrata viene risposto con un elemento della cache precompilato. Il seguente codice utilizza il gestore eventi fetch
con il metodo match
dell'archiviazione della cache per rispondere solo alla cache:
self.addEventListener("fetch", event => {
event.respondWith(caches.match(event.request));
});
Strategie personalizzate
Sebbene quelle sopra indicate siano strategie di memorizzazione nella cache comuni, sei tu a occuparti del service worker e della gestione delle richieste. Se nessuno di questi soddisfa le tue esigenze, creane uno personalizzato.
Ad esempio, puoi utilizzare una strategia di priorità alla rete con un timeout per dare la priorità ai contenuti aggiornati, ma solo se la risposta viene visualizzata entro una soglia che hai impostato. Puoi anche unire una risposta memorizzata nella cache con una risposta di rete e creare una risposta complessa dal service worker.
Aggiornamento degli asset
Mantenere aggiornati gli asset memorizzati nella cache della PWA può essere difficile. Sebbene la strategia di rivalidazione durante l'inattività sia un modo per farlo, non è l'unico. Nel capitolo Aggiornamento imparerai diverse tecniche per mantenere aggiornati i contenuti e gli asset della tua app.