Pubblicazione

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

Browser Support

  • Chrome: 40.
  • Edge: 17.
  • Firefox: 44.
  • Safari: 11.1.

Source

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 si trova tra il client e la rete.

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.

Strategia Cache First

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.

La strategia Network First

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.

La strategia Stale-While-Revalidate

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.

Strategia Solo rete

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));
});

Strategia solo cache.

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.

Risorse