I service worker sono potenti e vale assolutamente la pena di imparare. Vi permettono di offrire un livello completamente nuovo di esperienza ai vostri utenti. Il vostro sito può caricarsi istantaneamente. Può funzionare offline. Può essere installato come un’app nativa e sentirsi altrettanto raffinato, ma con la portata e la libertà del web.

Ma i service worker sono diversi da qualsiasi cosa a cui la maggior parte di noi sviluppatori web è abituata. Vengono con una ripida curva di apprendimento e una manciata di intoppi a cui bisogna stare attenti.

Google Developers ed io abbiamo recentemente collaborato ad un progetto – Service Workies – un gioco gratuito per capire i lavoratori dei servizi. Mentre lo costruivo e lavoravo con i complessi ins e outs dei lavoratori di servizio, mi sono imbattuto in alcuni intoppi. Quello che mi ha aiutato di più è stato trovare una manciata di metafore descrittive. In questo post esploreremo questi modelli mentali e avvolgeremo il nostro cervello intorno ai tratti paradossali che rendono i lavoratori di servizio sia difficili che fantastici.

Lo stesso, ma diverso #

Mentre si codifica il lavoratore di servizio, molte cose sembreranno familiari. Puoi usare le tue nuove caratteristiche preferite del linguaggio JavaScript. Ascoltate gli eventi del ciclo di vita proprio come con gli eventi UI. Gestisci il flusso di controllo con le promesse come sei abituato a fare.

Ma altri comportamenti del service worker ti fanno grattare la testa per la confusione. Specialmente quando si aggiorna la pagina e non si vedono applicate le modifiche al codice.

Un nuovo livello #

Normalmente quando si costruisce un sito si hanno solo due livelli a cui pensare: il client e il server. Il service worker è un nuovo livello che si trova nel mezzo.

Pensate al vostro service worker come ad una sorta di estensione del browser, che il vostro sito può installare nel browser dell’utente. Una volta installato, il service worker estende il browser per il vostro sito con un potente strato intermedio. Questo strato di service worker può intercettare e gestire tutte le richieste che il tuo sito fa.

Lo strato di service worker ha il suo ciclo di vita indipendente dalla scheda del browser. Un semplice aggiornamento della pagina non è sufficiente per aggiornare un service worker, proprio come non ci si aspetta che un aggiornamento della pagina aggiorni il codice distribuito su un server. Ogni strato ha le sue regole uniche per l’aggiornamento.

Nel gioco Service Workies copriamo i molti dettagli del ciclo di vita del service worker e ti diamo una tonnellata di pratica per lavorarci.

Pensa al tuo service worker come a un nuovo strato intermedio con il suo ciclo di vita e i suoi metodi di aggiornamento.

Potente, ma limitato #

Avere un service worker sul tuo sito ti dà incredibili benefici. Il tuo sito può:

  • funzionare perfettamente anche quando l’utente è offline
  • ottenere massicci miglioramenti delle prestazioni attraverso il caching
  • utilizzare le notifiche push
  • essere installato come una PWA

Con tutto quello che i service worker possono fare, sono limitati dal design. Non possono fare nulla di sincrono o nello stesso thread del vostro sito. Questo significa nessun accesso a:

  • localStorage
  • il DOM
  • la finestra

La buona notizia è che ci sono una manciata di modi in cui la tua pagina può comunicare con il suo service worker, incluso il diretto postMessage, canali di messaggio uno-a-uno e canali di trasmissione uno-a-molti.

Pensa al tuo service worker come qualcosa che vive fuori dalla tua pagina. Puoi parlargli, ma non può accedere direttamente alla tua pagina.

Long-lived, but short-lived #

Un service worker attivo continua a vivere anche dopo che un utente lascia il tuo sito o chiude la scheda. Il browser mantiene questo service worker in giro in modo che sia pronto la prossima volta che l’utente torna sul tuo sito. Prima che venga fatta la prima richiesta, il service worker ha la possibilità di intercettarla e prendere il controllo della pagina. Questo è ciò che permette a un sito di funzionare offline: il service worker può servire una versione in cache della pagina stessa, anche se l’utente non ha connessione a internet.

In Service Workies visualizziamo questo concetto con Kolohe (un amichevole service worker) che intercetta e gestisce le richieste.

Stoppato #

Nonostante i service worker sembrino essere immortali, possono essere fermati in qualsiasi momento. Il browser non vuole sprecare risorse su un service worker che attualmente non sta facendo nulla. Essere fermati non è la stessa cosa che essere terminati: il service worker rimane installato e attivato. Viene solo messo a dormire. La prossima volta che è necessario (ad esempio, per gestire una richiesta), il browser lo risveglia.

waitUntil #

A causa della costante possibilità di essere messo a dormire, il vostro service worker ha bisogno di un modo per far sapere al browser quando sta facendo qualcosa di importante e non ha voglia di fare un pisolino. È qui che entra in gioco event.waitUntil(). Questo metodo estende il ciclo di vita in cui è utilizzato, impedendogli sia di essere fermato che di passare alla fase successiva del suo ciclo di vita fino a quando non siamo pronti. Questo ci dà il tempo di impostare le cache, recuperare le risorse dalla rete, ecc.

Questo esempio dice al browser che il nostro service worker non ha finito di installarsi finché la cache assets non è stata creata e popolata con l’immagine di una spada:

self.addEventListener("install", event => {
event.waitUntil(
caches.open("assets").then(cache => {
return cache.addAll();
})
);
});

Attenzione allo stato globale #

Quando avviene questo start/stop lo scope globale del service worker viene resettato. Quindi fate attenzione a non usare alcuno stato globale nel vostro service worker o sarete tristi la prossima volta che si risveglierà e avrà uno stato diverso da quello che si aspettava.

Considerate questo esempio che usa uno stato globale:

const favoriteNumber = Math.random();
let hasHandledARequest = false;
self.addEventListener("fetch", event => {
console.log(favoriteNumber);
console.log(hasHandledARequest);
hasHandledARequest = true;
});

Ad ogni richiesta questo service worker registrerà un numero, diciamo 0.13981866382421893. La variabile hasHandledARequest cambia anche in true. Ora il service worker rimane inattivo per un po’, quindi il browser lo ferma. La prossima volta che c’è una richiesta, il service worker è di nuovo necessario, quindi il browser lo risveglia. Il suo script viene valutato di nuovo. Ora hasHandledARequest viene reimpostato a false, e favoriteNumber è qualcosa di completamente diverso-0.5907281835659033.

Non si può fare affidamento sullo stato memorizzato in un service worker. Inoltre, creare istanze di cose come Message Channels può causare bug: otterrete un’istanza nuova di zecca ogni volta che il service worker si ferma/avvia.

Attenzione: Questo intoppo è particolarmente importante da tenere a mente mentre si lavora sul codice del service worker perché quando Chrome DevTools è aperto, il comportamento di start/stop è disabilitato. Potresti anche non vedere i bug causati dall’affidarsi allo stato globale fino a quando non sono stati spediti ai tuoi utenti.

Nel capitolo 3 di Service Workies visualizziamo il nostro service worker fermo come se perdesse tutto il colore mentre aspetta di essere svegliato.

Pensa al tuo service worker come a un cane da caccia. È veloce, leale e fantastico. Rimarrà al tuo fianco in ogni caso. Ma soprattutto vuole solo dormire. Sempre. Devi fargli capire quando vuoi che resti sveglio. Bravo cane!

Insieme, ma separati #

La tua pagina può essere controllata solo da un service worker alla volta. Ma può avere due service worker installati contemporaneamente. Quando fai una modifica al codice del tuo service worker e aggiorni la pagina, in realtà non stai modificando affatto il tuo service worker. I service worker sono immutabili. Ne state invece creando uno nuovo di zecca. Questo nuovo service worker (chiamiamolo SW2) si installerà ma non si attiverà ancora. Deve aspettare che l’attuale service worker (SW1) termini (quando l’utente lascia il sito).

Mischiarsi con le cache di un altro service worker #

Durante l’installazione, SW2 può impostare le cose, di solito creando e popolando le cache. Ma attenzione: questo nuovo service worker ha accesso a tutto ciò a cui ha accesso l’attuale service worker. Se non stai attento, il tuo nuovo service worker in attesa può davvero incasinare le cose per il tuo service worker attuale. Alcuni esempi che potrebbero causarvi problemi:

  • SW2 potrebbe cancellare una cache che SW1 sta utilizzando attivamente.
  • SW2 potrebbe modificare il contenuto di una cache che SW1 sta utilizzando, facendo sì che SW1 risponda con risorse che la pagina non si aspetta.

Skip skipWaiting #

Un service worker può anche usare il rischioso metodo skipWaiting() per prendere il controllo della pagina non appena ha finito l’installazione. Questa è generalmente una cattiva idea, a meno che non si stia intenzionalmente cercando di sostituire un service worker buggato. Il nuovo service worker potrebbe usare risorse aggiornate che la pagina corrente non si aspetta, portando a errori e bug.

Iniziare in modo pulito #

Il modo per evitare che i tuoi service worker si aggroviglino a vicenda è assicurarsi che usino cache diverse. Il modo più semplice per farlo è quello di modificare i nomi delle cache che usano.

const version = 1;
const assetCacheName = `assets-${version}`;
self.addEventListener("install", event => {
caches.open(assetCacheName).then(cache => {
// confidently do stuff with your very own cache
});
});

Quando si distribuisce un nuovo service worker, si bump il version in modo che faccia ciò di cui ha bisogno con una cache completamente separata dal service worker precedente.

End clean #

Una volta che il service worker raggiunge lo stato activated, si sa che è subentrato, e il service worker precedente è ridondante (cioè, non è più necessario). A questo punto è importante ripulire il vecchio service worker. Non solo rispetta i limiti di memoria della cache dei tuoi utenti, ma può anche prevenire bug non intenzionali.

Il metodo caches.match() è una scorciatoia spesso usata per recuperare un elemento da qualsiasi cache in cui ci sia una corrispondenza. Ma itera attraverso le cache nell’ordine in cui sono state create. Quindi diciamo che hai due versioni di un file script app.js in due diverse cache –assets-1 e assets-2. La tua pagina si aspetta lo script più recente che è memorizzato in assets-2. Ma se non hai cancellato la vecchia cache, caches.match('app.js') restituirà quella vecchia di assets-1 e molto probabilmente romperà il tuo sito.

Tutto ciò che serve per ripulire dopo i precedenti service worker è cancellare ogni cache di cui il nuovo service worker non ha bisogno:

const version = 2;
const assetCacheName = `assets-${version}`;
self.addEventListener("activate", event => {
event.waitUntil(
caches.keys().then(cacheNames => {
return Promise.all(
cacheNames.map(cacheName => {
if (cacheName !== assetCacheName){
return caches.delete(cacheName);
}
});
);
});
);
});

Evitare che i tuoi service worker si scannino a vicenda richiede un po’ di lavoro e disciplina, ma ne vale la pena.

Pensa alla combinazione del tuo service worker e del tuo sito come un’app installabile. Ogni versione dovrebbe funzionare. Ogni versione dovrebbe essere separata dalle altre. Immaginate quanto sarebbe buggato un gioco se lo sviluppatore rilasciasse accidentalmente una patch che usa una nuova logica di gioco ma risorse obsolete. Si infurierebbe sui forum così velocemente! Mantenete le versioni delle vostre app ordinate &pulite.

Mentalita’ del lavoratore di servizio #

Imparare la giusta mentalita’ mentre pensate ai lavoratori di servizio vi aiutera’ a costruire i vostri con fiducia. Una volta che hai capito come funziona, sarai in grado di creare esperienze incredibili per i tuoi utenti.

Se vuoi capire tutto questo giocando a un gioco, allora sei fortunato! Vai a giocare a Service Workies dove imparerai i modi del lavoratore di servizio per uccidere le bestie offline.

Ultimo aggiornamento: Jun 4, 2019 Migliorare l’articolo

.

Lascia un commento

Il tuo indirizzo email non sarà pubblicato.