Pubblicazione

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

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 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 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 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.

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

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.

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 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.

La strategia obsoleta durante la convalida

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

Strategia solo cache.

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.

Risorse