Crea una PWA con Webpack e Workbox
Pubblicato: 2022-03-10Un'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:
- È disponibile un'interfaccia a riga di comando che ti consente di integrare la casella di lavoro in qualsiasi applicazione di cui disponi;
- È disponibile un modulo Node.js che ti consente di integrare la casella di lavoro in qualsiasi strumento di compilazione Node come gulp o grunt;
- È 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.
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:
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:
E qui ci sono esempi di shell di app da PWA:
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:
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.
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:
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.
Questo è vantaggioso perché ora possiamo reindirizzare questa richiesta non riuscita alla cache (supponendo che abbiamo il contenuto nella cache).
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.
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!
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:
-
CacheFirst
-
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.
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'IDworkbox-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