Code

Service Worker: un valido aiuto per la vostra PWA

2 Settembre 2022 - 4 minuti di lettura

Vorresti sviluppare una Progressive Web App (PWA) che migliori l’esperienza utente? Un Service Worker è ciò che fa per te!

In questo breve articolo vi parlerò del Service Worker, delle sue funzionalità principali e del suo ciclo di vita. Vi fornirò inoltre un semplice codice di esempio per l’installazione e l’attivazione.

Che cos’è un Service Worker

Un Service Worker è essenzialmente un file JavaScript che viene eseguito su un thread separato rispetto a quello principale del browser. Questo componente risulta essere molto utile in quanto è in grado d’intercettare richieste dalla rete, salvare/ritrovare dati nella cache e inviare notifiche push all’utente. È quindi un componente fondamentale da implementare qualora si volesse sviluppare una PWA.

Possiamo considerare un Service Worker come un miglioramento dell’esperienza utente durante la navigazione. Qualora un utente dovesse utilizzare un browser obsoleto che non supporta i Service Worker, tutte le funzionalità base della PWA sarebbero comunque garantite.

Funzionalità principali di un Service Worker

Un Service Worker possiede una serie di funzionalità, di seguito vi elenco quelle principali.

  • Agisce come proxy tra il browser Web e il server Web permettendo di controllare e gestire le richieste a una pagina Web.
  • Funziona solamente con il protocollo HTTPS.
  • Passa in stato idle quando non è utilizzato e si riattiva quando è necessario, non esiste quindi uno stato globale persistente tra differenti eventi. Per salvare le informazioni da riutilizzare successivamente, si fa uso degli IndexedDB.
  • Offre due API per permettere a un’applicazione di lavorare offline:
    • fetch: il modo standard per ritrovare informazioni dalla rete;
    • cache: per memorizzare precedenti richieste e velocizzare quelle future.
  • È un oggetto event driven, ovvero ciascun task è lanciato in risposta a uno specifico evento.

Ciclo di vita di un Service Worker

Il lifecycle è molto simile a quella di un’applicazione per smartphone. Ogni volta che installiamo una app da uno store:

  • viene fatta una richiesta per scaricare l’applicazione;
  • l’applicazione viene quindi installata al termine del download;
  • l’applicazione è pronta all’uso e può essere eseguita;
  • ogni volta che vengono pubblicati nuovi rilasci l’applicazione si aggiorna.

Analogamente il ciclo di vita di un Service Worker si articola in tre fasi.

  • Registrazione
  • Installazione
  • Attivazione

Registrazione

Prima d’installare un Service Worker, occorre registrarlo all’interno del file JavaScript del main dell’applicazione così che il browser possa essere informato della sua presenza. Questa fase ritorna una promise che viene risolta nel momento in cui il componente termina la registrazione con successo.
Lo scope determina quali file avrà sotto il suo controllo, in altre parole da quale percorso (quale route) intercetterà le richieste. Lo scope di default è la location del file, e si estende a tutte le sue sottocartelle. Per esempio, se il file service-worker.js fosse situato nella cartella root del progetto, esso controllerebbe tutte le richieste a tutti i file del dominio.

È possibile cambiare lo scope di default del Service Worker passandogli un parametro addizionale alla chiamata di registrazione.

Installazione

Terminata la fase di registrazione si può procedere con l’installazione. Questa avviene quando il Service Worker risulta essere nuovo al browser oppure anche quando risulta esserci una differenza tra il precedente installato e quello nuovo, quindi quando è necessario un aggiornamento.
Questa fase genera un evento install che è possibile intercettare mediante un listener per eseguire alcuni task durante l’installazione. Per esempio è utile inserire nella cache parti della Web app che risultano essere statiche e che verranno richieste frequentemente.

Di seguito vi riporto un codice di esempio di semplice installazione:

const staticCacheName = 'pages-cache-v1';

const filesToCache = [
  './images/1.jpg',
  './images/2.jpg',
  './images/3.jpg'
];

self.addEventListener('install', event => {
  console.log('Attempting to install service worker and cache static assets');
  event.waitUntil(
    caches.open(staticCacheName)
    .then(cache => {
      console.log('Adding static files to cache')
      return cache.addAll(filesToCache);
    })
  );
});

Attivazione

In questa fase, se ci sono delle pagine dell’app controllate da una versione precedente del Service Worker, la nuova entra in uno stato waiting e si attiverà solamente quando non ci saranno più pagine gestite dalla precedente versione. In questo modo è garantito che ci sia solamente una sola versione attiva contemporaneamente.
Quando il componente si attiva, scatena un evento activate che può essere utile intercettare per pulire dati vecchi presenti nella cache.

Una volta attivo, è possibile esaminare l’oggetto navigando nell’apposita sezione tra i Web development tools del browser usato per l’app.

Di seguito vi riporto il codice di esempio che gestisce il salvataggio di dati in cache:

self.addEventListener('fetch', event => {
   event.respondWith(
      caches.match(event.request)
      .then(response => {
         if (response) {
            console.log('found ', event.request.url, ' in cache');
            return response;
         }
         console.log('Resource not found in cache');
         console.log('Network request for ',event.request.url);
         return fetch(event.request)
         .then(response => {
            // we need to save clone to put one copy in cache and serve second one
            let responseClone = response.clone();
            caches.open('v1').then(function (cache) {
               caches.out(event.request, responseClone);
            });
            return response;   
         });
      }).catch(error => {
         return new Response("Network error happened", {"status" : 408, "headers" : {"Content-Type" : "text/plain"}});
      }
   }; 
});

Conclusioni

Spero che con questo articolo possa avervi incuriosito sull’importanza e l’utilizzo dei Service Worker. Qualora voleste approfondire, vi lascio due link inerenti alla documentazione ufficiale.

Questo invece è l’articolo “The Offline Cookbook” che tratta dei pattern più comuni per salvare risorse nella cache del browser.

Articolo scritto da