In uso

Un aspetto chiave delle app web progressive è l'affidabilità; sono in grado di caricare gli asset rapidamente, mantenere coinvolti gli utenti e fornire feedback immediatamente, anche in condizioni di rete avverse. Com'è possibile? Grazie all'evento fetch del service worker.

L'evento di recupero

Supporto dei browser

  • 40
  • 17
  • 44
  • 11.1

Fonte

L'evento fetch ci consente di intercettare ogni richiesta di rete effettuata dalla PWA nell'ambito del service worker, sia per le richieste dalla stessa origine che per quelle multiorigine. Oltre alle richieste di navigazione e di asset, il recupero da un service worker installato consente di eseguire il rendering delle 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 precedentemente memorizzata nella cache o creare una nuova risposta. Sta a te scegliere. Ecco un semplice esempio:

self.addEventListener("fetch", event => {
    console.log(`URL requested: ${event.request.url}`);
});

Rispondere a una richiesta

Quando ricevi una richiesta nel Service worker, puoi eseguire due operazioni; puoi ignorarla e quindi passare alla rete oppure rispondere. Rispondere alle richieste dall'interno del service worker è il modo in cui puoi scegliere cosa e come viene restituito 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 devi restituire un oggetto Response. Tuttavia, non puoi chiamare respondWith() al termine del gestore dell'evento fetch, ad esempio nell'ambito di una chiamata asincrona. Se devi attendere la risposta completa, puoi trasmettere una promessa a respondWith() che si risolve con una Risposta.

Creazione delle risposte

Grazie all'API Fetch puoi creare risposte HTTP nel codice JavaScript; tali 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, impostandone il corpo e opzioni come stato e 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 gestire le risposte HTTP da un service worker, è il momento di utilizzare l'interfaccia Memorizzazione nella cache 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 questa funzionalità. Per farlo, devi prima cercare nella cache. La funzione match(), disponibile nell'interfaccia caches di primo livello, cerca in tutti i negozi nell'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 rimuovere la cache. Ecco perché dovresti definire le tue strategie per la pubblicazione di asset per la tua PWA. Non hai la limitazione di una sola strategia di memorizzazione nella cache. Puoi definirne di diverse per pattern URL differenti. Ad esempio, puoi avere una strategia per gli asset UI minimi, un'altra per le chiamate API e una terza per gli URL immagine e di dati. Per farlo, leggi event.request.url in ServiceWorkerGlobalScope.onfetch e analizzalo tramite espressioni regolari o un pattern URL. Al momento della scrittura, il pattern URL non è supportato su tutte le piattaforme.

Le strategie più comuni sono:

Prima cache
Prima cerca una risposta memorizzata nella cache, poi torna sulla rete se non ne viene trovata una.
Prima rete
Richiede prima una risposta dalla rete e, se non ne viene restituita nessuna, verifica la risposta nella cache.
Non aggiornato durante la riconvalida
Pubblica una risposta dalla cache, mentre in background richiede l'ultima versione e la salva nella cache per la successiva richiesta dell'asset.
Solo rete
Risponde sempre con una risposta dalla rete o in caso di errori. La cache non viene mai consultata.
Solo cache
Risponde sempre con una risposta dalla cache o con errori in uscita. La rete non verrà mai contattata. Gli asset che verranno pubblicati utilizzando questa strategia devono essere aggiunti alla cache prima di essere richiesti.

Prima cache

Utilizzando questa strategia, il service worker cerca la richiesta corrispondente nella cache e, se è memorizzata nella cache, restituisce la risposta corrispondente. In caso contrario, recupera la risposta dalla rete (facoltativamente, aggiornando 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 degli asset senza passare alla rete tende a essere più veloce, questa strategia dà priorità alle prestazioni rispetto all'aggiornamento.

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

Prima rete

Questa strategia è il mirroring della strategia Cache First: verifica se la richiesta può essere soddisfatta dalla rete e, in caso contrario, prova a recuperarla dalla cache. Come prima la cache. Se non è presente né una risposta di rete né una risposta della cache, la richiesta restituirà un errore. In genere è più lento che ottenere la risposta dalla rete che dalla cache. Questa strategia dà 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) ;
     })
   );
});

Non aggiornato durante la riconvalida

La strategia inattiva mentre riconvalida restituisce immediatamente una risposta memorizzata nella cache, quindi verifica la presenza di un aggiornamento sulla rete, sostituendo la risposta memorizzata nella cache, se presente. Questa strategia effettua sempre una richiesta di rete perché, anche se viene trovata una risorsa memorizzata nella cache, prova ad aggiornare ciò che era presente nella cache con ciò che è stato ricevuto dalla rete, in modo da utilizzare la versione aggiornata nella richiesta successiva. Questa strategia, pertanto, offre un modo per trarre vantaggio dalla pubblicazione rapida della strategia basata sulla cache e aggiornare la cache in background.

Strategia obsoleta durante la riconvalida

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.

La strategia Solo rete

Solo cache

La strategia Solo cache garantisce che le richieste non vengano mai inviate alla rete, mentre a tutte le richieste in entrata viene risposto con un elemento della cache precompilato. Il seguente codice utilizza il gestore di 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));
});

Strategia di sola memorizzazione nella cache.

Strategie personalizzate

Sebbene le precedenti siano strategie di memorizzazione nella cache comuni, sei responsabile del service worker e della gestione delle richieste. Se nessuna di queste soluzioni soddisfa le tue esigenze, creane una personalizzata.

Ad esempio, puoi utilizzare una strategia basata sulla rete con un timeout per dare la priorità ai contenuti aggiornati, ma solo se la risposta viene visualizzata entro la soglia che hai impostato. È inoltre possibile unire una risposta memorizzata nella cache con una risposta di rete e generare una risposta complessa dal service worker.

Aggiornamento degli asset

Mantenere aggiornati gli asset memorizzati nella cache della PWA può essere una sfida. Sebbene sia uno dei modi per farlo, anche se la strategia di riconvalida è obsoleta, non è l'unico. Nel capitolo Aggiornamento scoprirai diverse tecniche per mantenere aggiornati i contenuti e le risorse della tua app.

Risorse