Crea una PWA con Webpack e Workbox

Pubblicato: 2022-03-10
Riepilogo rapido ↬ Questo tutorial ti aiuterà a trasformare un'app che non funziona offline in una PWA che funziona offline e mostra un'icona di aggiornamento disponibile. Imparerai come memorizzare nella cache le risorse con la casella di lavoro, gestire la memorizzazione nella cache dinamica e gestire gli aggiornamenti alla tua PWA. Segui e scopri come puoi applicare queste tecniche anche sul tuo sito web.

Un'app Web progressiva (PWA) è un sito che utilizza la tecnologia moderna per offrire esperienze simili a quelle di un'app sul Web. È un termine generico per nuove tecnologie come "manifesto dell'app Web", "operatore di servizio" e altro ancora. Se unite insieme, queste tecnologie ti consentono di offrire esperienze utente rapide e coinvolgenti con il tuo sito web.

Questo articolo è un'esercitazione dettagliata per l'aggiunta di un lavoratore del servizio a un sito Web di una pagina esistente. L'operatore del servizio ti consentirà di far funzionare offline il tuo sito Web notificando anche agli utenti gli aggiornamenti del tuo sito. Tieni presente che si basa su un piccolo progetto in bundle con Webpack, quindi utilizzeremo il plug-in Workbox Webpack (Workbox v4).

L'utilizzo di uno strumento per generare il lavoratore del servizio è l'approccio consigliato in quanto consente di gestire la cache in modo efficiente. Utilizzeremo Workbox, un insieme di librerie che semplificano la generazione del codice del lavoratore del servizio, per generare il nostro lavoratore del servizio in questo tutorial.

A seconda del tuo progetto, puoi utilizzare Workbox in tre modi diversi:

  1. È disponibile un'interfaccia a riga di comando che ti consente di integrare la casella di lavoro in qualsiasi applicazione di cui disponi;
  2. È disponibile un modulo Node.js che ti consente di integrare la casella di lavoro in qualsiasi strumento di compilazione Node come gulp o grunt;
  3. È disponibile un plug-in webpack che ti consente di integrarti facilmente con un progetto creato con Webpack.

Webpack è un raggruppatore di moduli. Per semplificare, puoi pensarlo come uno strumento che gestisce le tue dipendenze JavaScript. Ti consente di importare codice JavaScript dalle librerie e raggruppa il tuo JavaScript in uno o più file.

Altro dopo il salto! Continua a leggere sotto ↓

Per iniziare, clona il seguente repository sul tuo computer:

 git clone [email protected]:jadjoubran/workbox-tutorial-v4.git cd workbox-tutorial-v4 npm install npm run dev

Quindi, vai a https://localhost:8080/ . Dovresti essere in grado di vedere l'app valuta che utilizzeremo durante questo tutorial:

Screenshot dell'app valuta che stiamo costruendo in questo articolo.
'Valute' è una PWA che elenca le commissioni di conversione delle valute rispetto alla valuta Euro (€). (Grande anteprima)

Inizia con un'app Shell

La shell dell'applicazione (o "shell dell'app") è un modello ispirato alle app native. Aiuterà a dare alla tua app un aspetto più nativo. Fornisce semplicemente all'app un layout e una struttura senza dati, una schermata di transizione che mira a migliorare l'esperienza di caricamento della tua app web.

Ecco alcuni esempi di shell di app da app native:

Shell dell'app Posta in arrivo di Google
Shell dell'app Google Inbox: alcuni millisecondi prima che le e-mail vengano caricate nella shell dell'app. (Grande anteprima)
App Shell nativa di Twitter
App nativa di Twitter su Android: shell dell'app che mostra barra di navigazione, schede e caricatore. (Grande anteprima)

E qui ci sono esempi di shell di app da PWA:

App Shell della PWA di Twitter
La shell dell'app di PWA di Twitter (anteprima grande)
App Shell della PWA di Flipkart
La shell dell'app di PWA di Flipkart (anteprima grande)

Agli utenti piace l'esperienza di caricamento delle shell delle app perché disprezzano gli schermi vuoti. Una schermata vuota fa sentire all'utente che il sito Web non si sta caricando. Li fa sentire come se il sito Web fosse bloccato.

Le shell dell'app tentano di dipingere la struttura di navigazione dell'app il prima possibile, come la barra di navigazione, la barra delle schede e un caricatore che indica che il contenuto che hai richiesto è in fase di caricamento.

Quindi, come si crea una shell per app?

Il pattern della shell dell'app dà la priorità al caricamento di HTML, CSS e JavaScript che verranno visualizzati per primi. Ciò significa che dobbiamo dare a tali risorse la priorità assoluta, quindi è necessario integrare tali risorse. Quindi, per creare una shell dell'app, devi semplicemente incorporare HTML, CSS e JavaScript responsabili della shell dell'app. Ovviamente, non dovresti incorporare tutto, ma piuttosto rimanere entro un limite di circa 30-40 KB in totale.

Puoi vedere la shell dell'app inline in index.html . Puoi controllare il codice sorgente controllando il file index.html e puoi visualizzarlo in anteprima nel browser eliminando l'elemento <main> negli strumenti di sviluppo:

Valute PWA App Shell con barra di navigazione e caricatore
L'App Shell che stiamo costruendo in questo articolo. (Grande anteprima)

Funziona offline?

Simuliamo di andare offline! Apri DevTools, vai alla scheda di rete e seleziona la casella di controllo "Offline". Quando ricarichi la pagina, vedrai che otterremo la pagina offline del browser.

Pagina di errore offline del browser
La richiesta alla homepage non è riuscita, quindi ovviamente lo vediamo di conseguenza. (Grande anteprima)

Questo perché la richiesta iniziale a / (che caricherà il file index.html ) avrà esito negativo perché Internet è offline. L'unico modo per recuperare da tale richiesta non riuscita è avere un addetto al servizio.

Visualizziamo la richiesta senza un addetto al servizio:

Animazione di una richiesta di rete che va dal browser del cliente a Internet.
La richiesta di rete va dal browser a Internet e viceversa. (Icone da flaticon.com) (Anteprima grande)

Un service worker è un proxy di rete programmabile, il che significa che si trova tra la tua pagina web e Internet. Ciò ti consente di controllare le richieste di rete in entrata e in uscita.

Animazione di una richiesta di rete intercettata dall'operatore del servizio.
La richiesta di rete viene intercettata dall'operatore del servizio. (Icone da flaticon.com) (Anteprima grande)

Questo è vantaggioso perché ora possiamo reindirizzare questa richiesta non riuscita alla cache (supponendo che abbiamo il contenuto nella cache).

Animazione di una richiesta di rete intercettata dal service worker e reindirizzata alla cache.
La richiesta di rete viene reindirizzata alla cache quando esiste già nella cache. (Icone da flaticon.com) (Anteprima grande)

Un Service worker è anche un tipo di Web Worker, il che significa che viene eseguito separatamente dalla pagina principale e non ha accesso né alla window né all'oggetto del document .

Precache l'app Shell

Per far funzionare la nostra app offline, inizieremo memorizzando nella cache la shell dell'app.

Quindi iniziamo installando il plug-in Webpack Workbox:

 npm install --save-dev workbox-webpack-plugin

Quindi apriremo il nostro file index.js e registreremo il lavoratore del servizio:

 if ("serviceWorker" in navigator){ window.addEventListener("load", () => { navigator.serviceWorker.register("/sw.js"); }) }

Quindi, apri il file webpack.config.js e configuriamo il plug-in webpack di Workbox:

 //add at the top const WorkboxWebpackPlugin = require("workbox-webpack-plugin"); //add inside the plugins array: plugins: [ … , new WorkboxWebpackPlugin.InjectManifest({ swSrc: "./src/src-sw.js", swDest: "sw.js" }) ]

Questo indicherà a Workbox di utilizzare il nostro file ./src/src-sw.js come base. Il file generato si chiamerà sw.js e si troverà nella cartella dist .

Quindi crea un file ./src/src-sw.js a livello di root e scrivi quanto segue al suo interno:

 workbox.precaching.precacheAndRoute(self.__precacheManifest);

Nota : la variabile self.__precacheManifest verrà importata da un file che verrà generato dinamicamente dalla casella di lavoro.

Ora sei pronto per creare il tuo codice con npm run build e Workbox genererà due file all'interno della cartella dist :

  • precache-manifest.66cf63077c7e4a70ba741ee9e6a8da29.js
  • sw.js

sw.js importa la casella di lavoro dalla CDN così come il precache-manifest.[chunkhash].js .

 //precache-manifest.[chunkhash].js file self.__precacheManifest = (self.__precacheManifest || []).concat([ "revision": "ba8f7488757693a5a5b1e712ac29cc28", "url": "index.html" }, "url": "main.49467c51ac5e0cb2b58e.js" ]);

Il manifest precache elenca i nomi dei file che sono stati elaborati da webpack e che finiscono nella cartella dist . Utilizzeremo questi file per memorizzarli nella cache nel browser. Ciò significa che quando il tuo sito Web viene caricato per la prima volta e registra il lavoratore del servizio, memorizzerà nella cache queste risorse in modo che possano essere utilizzate la volta successiva.

Puoi anche notare che alcune voci hanno una "revisione" mentre altre no. Questo perché la revisione a volte può essere dedotta dal chunkhash dal nome del file. Ad esempio, diamo un'occhiata più da vicino al nome del file main.49467c51ac5e0cb2b58e.js . Ha una revisione nel nome del file, che è il chunkhash 49467c51ac5e0cb2b58e .

Ciò consente a Workbox di capire quando i tuoi file cambiano in modo che pulisca o aggiorni solo i file che sono stati modificati, invece di scaricare tutta la cache ogni volta che pubblichi una nuova versione del tuo service worker.

La prima volta che carichi la pagina, l'operatore del servizio verrà installato. Puoi vederlo in DevTools. Innanzitutto, viene richiesto il file sw.js che quindi richiede tutti gli altri file. Sono chiaramente contrassegnati dall'icona a forma di ingranaggio.

Screenshot della scheda Rete di DevTools quando viene installato il lavoratore del servizio.
Le richieste contrassegnate con l'icona ️ sono richieste avviate dall'operatore del servizio. (Grande anteprima)

Quindi Workbox si inizializzerà e memorizzerà nella cache tutti i file che si trovano nel precache-manifest. È importante ricontrollare di non avere file non necessari nel file manifest di precache, ad esempio file .map o file che non fanno parte della shell dell'app.

Nella scheda rete, possiamo vedere le richieste provenienti dal lavoratore del servizio. E ora se provi ad andare offline, la shell dell'app è già memorizzata nella cache, quindi funziona anche se siamo offline!

Screenshot della scheda Rete di Dev Tools che mostra le chiamate API non riuscite.
Le chiamate API non riescono quando siamo offline. (Grande anteprima)

Cache percorsi dinamici

Hai notato che quando siamo andati offline, la shell dell'app funziona ma non i nostri dati? Questo perché queste chiamate API non fanno parte della shell dell'app precache . Quando non c'è connessione a Internet, queste richieste non riusciranno e l'utente non sarà in grado di vedere le informazioni sulla valuta.

Tuttavia, queste richieste non possono essere memorizzate nella cache perché il loro valore deriva da un'API. Inoltre, quando inizi ad avere più pagine, non vuoi memorizzare nella cache tutte le richieste API in una volta sola. Invece, vuoi memorizzarli nella cache quando l'utente visita quella pagina.

Li chiamiamo "dati dinamici". Spesso includono chiamate API, immagini e altre risorse che vengono richieste quando un utente esegue una determinata azione sul tuo sito web (ad esempio quando naviga in una nuova pagina).

Puoi memorizzarli nella cache utilizzando il modulo di routing di Workbox. Ecco come:

 //add in src/src-sw.js workbox.routing.registerRoute( /https:\/\/api\.exchangeratesapi\.io\/latest/, new workbox.strategies.NetworkFirst({ cacheName: "currencies", plugins: [ new workbox.expiration.Plugin({ maxAgeSeconds: 10 * 60 // 10 minutes }) ] }) );

Ciò imposterà la memorizzazione nella cache dinamica per qualsiasi URL di richiesta che corrisponda all'URL https://api.exchangeratesapi.io/latest .

La strategia di memorizzazione nella cache che abbiamo utilizzato qui si chiama NetworkFirst ; ce ne sono altri due che vengono usati spesso:

  1. CacheFirst
  2. StaleWhileRevalidate

CacheFirst lo cercherà prima nella cache. Se non viene trovato, lo riceverà dalla rete. StaleWhileRevalidate andrà alla rete e alla cache contemporaneamente. Restituisce la risposta della cache alla pagina (mentre è in background) utilizzerà la nuova risposta di rete per aggiornare la cache per il prossimo utilizzo.

Per il nostro caso d'uso, abbiamo dovuto scegliere NetworkFirst perché abbiamo a che fare con tassi di cambio che cambiano molto spesso. Tuttavia, quando l'utente va offline, possiamo almeno mostrargli le tariffe come erano 10 minuti fa: ecco perché abbiamo utilizzato il plug-in di scadenza con maxAgeSeconds impostato su 10 * 60 secondi.

Gestisci gli aggiornamenti delle app

Ogni volta che un utente carica la tua pagina, il browser eseguirà il codice navigator.serviceWorker.register anche se il service worker è già installato e in esecuzione. Ciò consente al browser di rilevare se è disponibile una nuova versione del lavoratore del servizio. Quando il browser rileva che il file non è cambiato, salta semplicemente la chiamata di registrazione. Una volta modificato il file, il browser comprende che esiste una nuova versione del lavoratore del servizio, quindi installa il nuovo lavoratore del servizio parallelamente al lavoratore del servizio attualmente in esecuzione .

Tuttavia, si interrompe nella fase di installed/waiting perché è possibile attivare un solo operatore di servizio alla volta.

Il ciclo di vita di un addetto ai servizi: analizzato, installato/in attesa, attivato e ridondante
Un ciclo di vita semplificato di un addetto ai servizi (anteprima grande)

Solo quando tutte le finestre del browser controllate dall'operatore del servizio precedente vengono chiuse, l'attivazione del nuovo operatore del servizio diventa sicura.

Puoi anche controllarlo manualmente chiamando skipWaiting() (o self.skipWaiting() poiché self è il contesto di esecuzione globale nel lavoratore del servizio). Tuttavia, la maggior parte delle volte dovresti farlo solo dopo aver chiesto all'utente se desidera ottenere l'ultimo aggiornamento.

Per fortuna, workbox-window ci aiuta a raggiungere questo obiettivo. È una nuova libreria di finestre introdotta in Workbox v4 che mira a semplificare le attività comuni sul lato della finestra.

Iniziamo installandolo con quanto segue:

 npm install workbox-window

Quindi, importa Workbox nella parte superiore del file index.js :

 import { Workbox } from "workbox-window";

Quindi sostituiremo il nostro codice di registrazione con il seguente:

 if ("serviceWorker" in navigator) { window.addEventListener("load", () => { const wb = new Workbox("/sw.js"); wb.register(); }); }

Troveremo quindi il pulsante di aggiornamento che ha l'ID aggiornamento dell'app e ascolta l' workbox-waiting della casella di lavoro:

 //add before the wb.register() const updateButton = document.querySelector("#app-update"); // Fires when the registered service worker has installed but is waiting to activate. wb.addEventListener("waiting", event => { updateButton.classList.add("show"); updateButton.addEventListener("click", () => { // Set up a listener that will reload the page as soon as the previously waiting service worker has taken control. wb.addEventListener("controlling", event => { window.location.reload(); }); // Send a message telling the service worker to skip waiting. // This will trigger the `controlling` event handler above. wb.messageSW({ type: "SKIP_WAITING" }); }); });

Questo codice mostrerà il pulsante di aggiornamento quando c'è un nuovo aggiornamento (quindi quando il lavoratore del servizio è in stato di attesa) e invierà un messaggio SKIP_WAITING al lavoratore del servizio.

Avremo bisogno di aggiornare il file di lavoro del servizio e gestire l'evento SKIP_WAITING in modo tale che chiami skipWaiting :

 //add in src-sw.js addEventListener("message", event => { if (event.data && event.data.type === "SKIP_WAITING") { skipWaiting(); });

Ora esegui npm run dev quindi ricarica la pagina. Entra nel tuo codice e aggiorna il titolo della barra di navigazione su "Navbar v2". Ricarica di nuovo la pagina e dovresti essere in grado di vedere l'icona di aggiornamento.

Avvolgendo

Il nostro sito Web ora funziona offline ed è in grado di informare l'utente sui nuovi aggiornamenti. Tieni presente, tuttavia, che il fattore più importante durante la creazione di una PWA è l'esperienza dell'utente. Concentrati sempre sulla creazione di esperienze facili da utilizzare per i tuoi utenti. Noi, come sviluppatori, tendiamo a essere troppo entusiasti della tecnologia e spesso finiamo per dimenticare i nostri utenti.

Se desideri fare un ulteriore passo avanti, puoi aggiungere un manifest dell'app Web che consentirà ai tuoi utenti di aggiungere il sito alla loro schermata iniziale. E se desideri saperne di più su Workbox, puoi trovare la documentazione ufficiale sul sito Web di Workbox.

Ulteriori letture su SmashingMag:

  • Puoi guadagnare di più con un'app mobile o una PWA?
  • Una guida completa alle applicazioni Web progressive
  • Native e PWA: scelte, non sfidanti!
  • Costruire una PWA usando Angular 6