Gestione

Un aspetto fondamentale delle app web progressive è la loro affidabilità; possono caricare rapidamente gli asset, mantenendo coinvolti gli utenti e fornendo feedback immediatamente, anche in cattive condizioni della rete. Com'è possibile? Grazie all'evento fetch del service worker.

Supporto dei browser

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

Origine

L'evento fetch ci consente di intercettare ogni richiesta di rete effettuata dalla PWA nell'ambito del service worker, sia per le richieste della stessa origine che per quelle multiorigine. Oltre alle richieste di navigazione e di asset, il recupero da parte di 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 tuo service worker può inoltrare una richiesta alla rete, rispondere con una risposta precedentemente memorizzata nella cache 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 arriva una richiesta al tuo service worker, puoi fare due cose: puoi ignorarlo, in modo che venga inviato alla rete, oppure rispondere. Rispondere alle richieste dall'interno del Service worker è un modo per scegliere cosa e come restituirlo alla PWA, anche quando l'utente è offline.

Per rispondere a una richiesta in arrivo, chiama event.respondWith() da un gestore di eventi fetch, come questo:

// 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 di eventi di recupero, ad esempio in 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 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 le 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 fornire le risposte HTTP da un service worker, puoi utilizzare l'interfaccia Archiviazione 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 tal caso, rispondere a respondWith() utilizzando questa informazione. Per farlo, devi prima effettuare una ricerca nella cache. La funzione match(), disponibile nell'interfaccia di primo livello caches, cerca in tutti gli archivi 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 il caricamento degli asset per la tua PWA. Non si è obbligati a usare una sola strategia di memorizzazione nella cache. Puoi definire parametri diversi per pattern URL differenti. Ad esempio, puoi avere una strategia per il numero minimo di asset UI, un'altra per le chiamate API e un terzo 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 documento, i pattern URL non sono supportati su tutte le piattaforme.

Le strategie più comuni sono:

Prima cache
Cerca prima una risposta memorizzata nella cache e, se non la trova, utilizza la rete.
Prima rete
Richiede prima una risposta dalla rete e, se non viene restituita, ne verifica la 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 dell'asset.
Solo rete
Risponde sempre con una risposta dalla rete o da un errore. La cache non viene mai consultata.
Solo cache
Risponde sempre con una risposta dalla cache o in caso di errore. La rete non verrà mai consultata. 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 restituisce la risposta corrispondente, se è stata memorizzata nella cache. In caso contrario, recupera la risposta dalla rete (facoltativamente, aggiornando la cache per le chiamate future). Se non è disponibile né una risposta della cache né una risposta di rete, la richiesta restituirà un errore. Poiché la pubblicazione degli asset senza indirizzare gli utenti sulla rete tende a essere più rapida, questa strategia dà la priorità al rendimento 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 rispecchia la strategia Cache First; verifica se la richiesta può essere soddisfatta dalla rete e, in caso contrario, prova a recuperarla dalla cache. Metti prima Mi piace alla cache. Se non c'è né una risposta di rete né una risposta della cache, la richiesta restituirà un errore. Ottenere la risposta dalla rete è in genere più lento rispetto al recupero dalla cache, poiché 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 non attiva durante la riconvalida restituisce immediatamente una risposta memorizzata nella cache, quindi controlla se nella rete è disponibile un aggiornamento, 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, proverà ad aggiornare ciò che si trovava nella cache con ciò che è stato ricevuto dalla rete per 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.

La strategia non aggiornata 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. Spesso questo è 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; tutte le richieste in entrata ricevono una risposta con un elemento della cache precompilato. Il codice seguente utilizza il gestore di eventi fetch con il metodo match dello spazio di archiviazione della cache solo per rispondere alla cache:

self.addEventListener("fetch", event => {
   event.respondWith(caches.match(event.request));
});

Strategia solo cache.

Strategie personalizzate

Sebbene le strategie di memorizzazione nella cache più comuni siano quelle più comuni, sei tu il responsabile del tuo service worker e di come vengono gestite le richieste. Se nessuna di queste soluzioni soddisfa le tue esigenze, creane una personalizzata.

Ad esempio, puoi utilizzare una strategia incentrata sulla rete con un timeout per dare priorità ai contenuti aggiornati, ma solo se la risposta appare entro una soglia da te impostata. Puoi anche unire una risposta memorizzata nella cache a una risposta di rete e creare una risposta complessa dal service worker.

Aggiornamento delle risorse

Mantenere aggiornati gli asset memorizzati nella cache della PWA può essere complicato. Sebbene la strategia non aggiornata durante la riconvalida sia un modo per farlo, non è l'unica. Nel capitolo Aggiornamento imparerai diverse tecniche per mantenere aggiornati i contenuti e le risorse della tua app.

Risorse