Integrazione delle tecniche di precaricamento tradizionali con i service worker.
L'esecuzione di un'attività su un sito di solito prevede diversi passaggi. Ad esempio, l'acquisto di un prodotto su un sito web di e-commerce potrebbe comportare la ricerca di un prodotto, la scelta di un articolo dall'elenco dei risultati, l'aggiunta dell'articolo al carrello e il completamento dell'operazione con il pagamento.
In termini tecnici, spostarsi tra pagine diverse significa effettuare una richiesta di navigazione. Come regola generale, non devi utilizzare intestazioni Cache-Control
permanenti per memorizzare nella cache la risposta HTML per una richiesta di navigazione. Normalmente dovrebbero essere soddisfatti tramite la rete, con Cache-Control: no-cache
, per garantire che il codice HTML, insieme alla catena di richieste di rete successive, sia (ragionevolmente) aggiornato.
Dover andare contro la rete ogni volta che l'utente passa a una nuova pagina purtroppo significa che ogni navigazione potrebbe essere lenta. Per lo meno, significa che non sarà affidabile.
Per velocizzare queste richieste, se puoi anticipare l'azione dell'utente, puoi richiedere queste pagine e questi asset in anticipo e mantenerli nella cache per un breve periodo di tempo finché l'utente non fa clic su questi link. Questa tecnica è chiamata prefetching e viene comunemente implementata aggiungendo tag <link rel="prefetch">
alle pagine, che indicano la risorsa da prelevare.
In questa guida esploreremo i diversi modi in cui i service worker possono essere utilizzati come complemento delle tecniche di prefetching tradizionali.
Casi di produzione
MercadoLibre è il più grande sito di e-commerce in America Latina. Per velocizzare le navigazioni, iniettano dinamicamente i tag <link rel="prefetch">
in alcune parti del flusso. Ad esempio, nelle pagine delle schede recuperano la pagina di risultati successiva non appena l'utente scorre fino in fondo alla scheda:
I file prerecuperati vengono richiesti con priorità "Più bassa" e memorizzati nella cache HTTP o nella cache della memoria (a seconda che la risorsa sia memorizzabile nella cache o meno), per un periodo di tempo che varia in base ai browser. Ad esempio, a partire da Chrome 85, questo valore è 5 minuti. Le risorse vengono conservate per cinque minuti, dopodiché vengono applicate le normali regole Cache-Control
per la risorsa.
L'utilizzo della memorizzazione nella cache del servizio worker può aiutarti a estendere la durata delle risorse di precaricamento oltre il periodo di cinque minuti.
Ad esempio, il portale sportivo italiano Virgilio Sport utilizza i service worker per eseguire il pre-caricamento dei post più popolari nella home page. Utilizzano inoltre l'API Network Information per evitare il pre-caricamento per gli utenti con una connessione 2G.
Di conseguenza, in oltre tre settimane di osservazione, Virgilio Sport ha registrato un miglioramento dei tempi di caricamento per la navigazione agli articoli del 78% e un aumento del 45% del numero di impressioni degli articoli.
Implementare il precaricamento con Workbox
Nella sezione seguente utilizzeremo Workbox per mostrare come implementare diverse tecniche di memorizzazione nella cache nel servizio worker che possono essere utilizzate come complemento di <link rel="prefetch">
o addirittura come sostituti, delegando completamente questa attività al servizio worker.
1. Prememorizza le pagine statiche e le risorse secondarie delle pagine
La precache è la capacità del service worker di salvare i file nella cache durante l'installazione.
Nei seguenti casi, il precaching viene utilizzato per raggiungere un obiettivo simile al prefetching: velocizzare la navigazione.
Precaricamento di pagine statiche
Per le pagine generate in fase di compilazione (ad es. about.html
, contact.html
) o in siti completamente statici, è sufficiente aggiungere i documenti del sito all'elenco di precache, in modo che siano già disponibili nella cache ogni volta che l'utente vi accede:
workbox.precaching.precacheAndRoute([
{url: '/about.html', revision: 'abcd1234'},
// ... other entries ...
]);
Precaricamento delle risorse secondarie della pagina
Il pre-caricamento degli asset statici che potrebbero essere utilizzati dalle diverse sezioni del sito (ad es. JavaScript, CSS e così via) è una best practice generale e può dare un'ulteriore spinta agli scenari di pre-caricamento.
Per velocizzare la navigazione in un sito di e-commerce, puoi utilizzare i tag <link rel="prefetch">
nelle pagine delle schede per prelevare le pagine dei dettagli dei prodotti per i primi prodotti di una pagina della scheda. Se hai già eseguito la precache delle risorse secondarie della pagina del prodotto, la navigazione può essere ancora più veloce.
Per implementare questa funzionalità:
- Aggiungi un tag
<link rel="prefetch">
alla pagina:
<link rel="prefetch" href="/phones/smartphone-5x.html" as="document">
- Aggiungi le risorse secondarie della pagina all'elenco di precaching nel service worker:
workbox.precaching.precacheAndRoute([
'/styles/product-page.ac29.css',
// ... other entries ...
]);
2. Estendere la durata delle risorse di prefetch
Come accennato in precedenza, <link rel="prefetch">
recupera e mantiene le risorse nella cache HTTP per un periodo di tempo limitato, dopodiché vengono applicate le regole Cache-Control
per una risorsa. A partire da Chrome 85, questo valore è 5 minuti.
I worker di servizio ti consentono di estendere la durata delle pagine di prefetch, offrendo al contempo il vantaggio aggiuntivo di rendere queste risorse disponibili per l'utilizzo offline.
Nell'esempio precedente, è possibile integrare <link rel="prefetch">
utilizzato per eseguire il pre-caricamento di una pagina di prodotto con una strategia di memorizzazione nella cache del runtime di Workbox.
Per implementare questa funzionalità:
- Aggiungi un tag
<link rel="prefetch">
alla pagina:
<link rel="prefetch" href="/phones/smartphone-5x.html" as="document">
- Implementa una strategia di memorizzazione nella cache in fase di esecuzione nel service worker per questi tipi di richieste:
new workbox.strategies.StaleWhileRevalidate({
cacheName: 'document-cache',
plugins: [
new workbox.expiration.Plugin({
maxAgeSeconds: 30 * 24 * 60 * 60, // 30 Days
}),
],
});
In questo caso, abbiamo scelto di utilizzare una strategia di aggiornamento dei dati non validi. In questa strategia, le pagine possono essere richieste sia dalla cache sia dalla rete, in parallelo. La risposta proviene dalla cache, se disponibile, altrimenti dalla rete. La cache viene sempre aggiornata con la risposta della rete a ogni richiesta andata a buon fine.
3. Delegare il pre-caricamento al service worker
Nella maggior parte dei casi, l'approccio migliore è utilizzare <link rel="prefetch">
. Il tag è un suggerimento per le risorse progettato per rendere il pre-caricamento il più efficiente possibile.
In alcuni casi, però, potrebbe essere meglio delegare completamente questa attività al service worker.
Ad esempio, per eseguire il pre-caricamento dei primi prodotti in una pagina di scheda di prodotto visualizzata lato client, potrebbe essere necessario inserire dinamicamente nella pagina diversi tag <link rel="prefetch">
in base a una risposta dell'API. Ciò può richiedere temporaneamente tempo sul thread principale della pagina e rendere più difficile l'implementazione.
In questi casi, utilizza una "strategia di comunicazione tra pagina e worker di servizio" per delegare completamente al worker di servizio il compito del pre-caricamento. Questo tipo di comunicazione può essere ottenuto utilizzando worker.postMessage():
Il pacchetto Workbox Window semplifica questo tipo di comunicazione, astraendo molti dettagli della chiamata sottostante in esecuzione.
Il precaricamento con la finestra Workbox può essere implementato nel seguente modo:
- Nella pagina: chiama il service worker passandogli il tipo di messaggio e l'elenco di URL da precaricare:
const wb = new Workbox('/sw.js');
wb.register();
const prefetchResponse = await wb.messageSW({type: 'PREFETCH_URLS', urls: […]});
- Nel service worker: implementa un gestore dei messaggi per emettere una richiesta
fetch()
per ogni URL da prelevare:
addEventListener('message', (event) => {
if (event.data.type === 'PREFETCH_URLS') {
// Fetch URLs and store them in the cache
}
});