Un aspetto chiave delle Progressive Web App è che sono affidabili: possono caricare rapidamente gli asset, coinvolgere gli utenti e fornire feedback immediati, anche in condizioni di rete scadenti. Come è 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 servizio worker, sia per le richieste con la stessa origine sia per quelle con origini diverse. Oltre alle richieste di navigazione e asset, il recupero da un servizio worker installato consente di visualizzare le visite alle pagine dopo il primo caricamento di un sito senza chiamate di rete.
L'handler 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 tuo service worker, puoi fare due cose: ignorarla, in modo che venga inoltrata alla rete, oppure puoi rispondere. Rispondere alle richieste dall'interno del tuo service worker ti consente di scegliere cosa e come viene restituito alla tua PWA, anche quando l'utente è offline.
Per rispondere a una richiesta in arrivo, chiama event.respondWith()
da un gestore eventi fetch
, ad esempio:
// 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 devi restituire un oggetto Response. Tuttavia, non puoi chiamare respondWith()
al termine del gestore dell'evento di recupero, ad esempio all'interno di una chiamata asincrona. Se devi attendere la risposta completa, puoi passare a respondWith()
una promessa che si risolve con una risposta.
Creazione di risposte
Grazie all'API Fetch, puoi creare risposte HTTP nel codice JavaScript, che 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
impostandone il corpo e le opzioni, come 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 inviare risposte HTTP da un servizio worker, è il momento di utilizzare l'interfaccia di archiviazione della cache per memorizzare gli asset sul dispositivo.
Puoi utilizzare l'API di archiviazione della cache per verificare se la richiesta ricevuta dalla PWA è disponibile nella cache e, in questo caso, rispondere a respondWith()
con essa.
Per farlo, devi prima eseguire una ricerca all'interno della cache. La funzione match()
, disponibile nell'interfaccia caches
di primo livello, esegue ricerche 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 si risolve 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 dei 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. Ecco perché devi definire le tue strategie per caricare gli 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, un'altra 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 di questo articolo, 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 ne trova una, torna alla rete.
- Prima la rete
- Richiede prima una risposta dalla rete e, se non ne viene restituita nessuna, controlla se è presente una risposta nella cache.
- Inattivo durante la riconvalida
- Fornisce una risposta dalla cache, mentre in background richiede la versione più recente e la salva nella cache per la successiva richiesta della risorsa.
- Solo rete
- Rispondi sempre con una risposta della rete o genera errori. La cache non viene mai consultata.
- Solo cache
- Rispondi sempre con una risposta dalla cache o genera errori. La rete non verrà mai consultata. Gli asset che verranno pubblicati utilizzando questa strategia devono essere aggiunti alla cache prima di essere richiesti.
Esegui prima la cache
Con 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 (se vuoi, aggiorna la cache per le chiamate future). Se non è presente né una risposta della cache né una risposta di rete, la richiesta restituirà un errore. Poiché la pubblicazione di asset senza passare dalla rete tende ad essere più rapida, questa strategia dà la priorità al rendimento rispetto all'aggiornamento.
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);
}
)
)
});
La rete prima di tutto
Questa strategia è il riflesso della strategia Cache First; controlla se la richiesta può essere soddisfatta dalla rete e, in caso contrario, tenta di recuperarla dalla cache. Come la cache. Se non è presente né una risposta di rete né una risposta della cache, la richiesta restituirà un errore. La risposta della rete è in genere più lenta rispetto a quella della 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) ;
})
);
});
Non aggiornato durante la riconvalida
La strategia di convalida se non aggiornata restituisce immediatamente una risposta memorizzata nella cache, quindi controlla la rete per verificare la presenza di un aggiornamento, sostituendo la risposta memorizzata nella cache se ne viene trovato uno. Questa strategia effettua sempre una richiesta di rete, perché anche se viene trovata una risorsa memorizzata nella cache, tenterà di aggiornare i dati memorizzati nella cache con quelli ricevuti dalla rete per utilizzare la versione aggiornata nella richiesta successiva. Questa strategia, quindi, ti consente di usufruire del caricamento rapido della strategia cache first 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
dello spazio di archiviazione della cache per rispondere solo alla cache:
self.addEventListener("fetch", event => {
event.respondWith(caches.match(event.request));
});
Strategie personalizzate
Sebbene quelle riportate sopra siano strategie di memorizzazione nella cache comuni, sei responsabile del tuo service worker e della modalità di gestione delle richieste. Se nessuna di queste opzioni soddisfa le tue esigenze, crea la tua.
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 impostata. Puoi anche unire una risposta memorizzata nella cache con una risposta di rete e creare una risposta complessa dal servizio worker.
Aggiornamento degli asset
Mantenere aggiornati gli asset memorizzati nella cache della tua PWA può essere una sfida. Sebbene la strategia di convalida in caso di dati non aggiornati 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.