Construiește un PWA cu Webpack și Workbox

Publicat: 2022-03-10
Rezumat rapid ↬ Acest tutorial vă va ajuta să transformați o aplicație care nu funcționează offline într-o PWA care funcționează offline și afișează o pictogramă de actualizare disponibilă. Veți învăța cum să predați în cache materialele cu workbox, să gestionați memorarea în cache dinamică, precum și să gestionați actualizările PWA. Urmăriți și vedeți cum puteți aplica și aceste tehnici pe site-ul dvs. web.

O aplicație web progresivă (PWA) este un site care utilizează tehnologia modernă pentru a oferi experiențe asemănătoare aplicațiilor pe web. Este un termen umbrelă pentru noile tehnologii, cum ar fi „manifestul aplicației web”, „lucrător de servicii” și multe altele. Atunci când sunt unite, aceste tehnologii vă permit să oferiți utilizatorilor experiențe rapide și captivante cu site-ul dvs. web.

Acest articol este un tutorial pas cu pas pentru adăugarea unui lucrător de service la un site web existent de o pagină. Lucrătorul de service vă va permite să faceți site-ul dvs. să funcționeze offline, notificând, de asemenea, utilizatorii cu privire la actualizările site-ului dvs. Vă rugăm să rețineți că acesta se bazează pe un proiect mic care este inclus cu Webpack, așa că vom folosi pluginul Workbox Webpack (Workbox v4).

Utilizarea unui instrument pentru a vă genera lucrătorul de service este abordarea recomandată, deoarece vă permite să vă gestionați eficient memoria cache. Vom folosi Workbox - un set de biblioteci care ușurează generarea codului dvs. de lucrător de service - pentru a genera lucrătorul nostru de service în acest tutorial.

În funcție de proiectul dvs., puteți utiliza Workbox în trei moduri diferite:

  1. Este disponibilă o interfață de linie de comandă care vă permite să integrați caseta de lucru în orice aplicație pe care o aveți;
  2. Este disponibil un modul Node.js care vă permite să integrați caseta de lucru în orice instrument de compilare Node, cum ar fi gulp sau grunt;
  3. Este disponibil un plugin webpack care vă permite să vă integrați cu ușurință cu un proiect care este construit cu Webpack.

Webpack este un bundler de module. Pentru a simplifica, vă puteți gândi la acesta ca la un instrument care vă gestionează dependențele JavaScript. Vă permite să importați cod JavaScript din biblioteci și să vă grupați JavaScript într-unul sau mai multe fișiere.

Mai multe după săritură! Continuați să citiți mai jos ↓

Pentru a începe, clonează următorul depozit pe computer:

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

Apoi, navigați la https://localhost:8080/ . Ar trebui să puteți vedea aplicația valutară pe care o vom folosi pe parcursul acestui tutorial:

Captură de ecran a aplicației valutare pe care o construim în acest articol.
„Monede” este un PWA care afișează taxele de conversie ale monedelor în raport cu moneda euro (€). (Previzualizare mare)

Începeți cu o aplicație Shell

Shell-ul aplicației (sau „app shell”) este un model inspirat din Native Apps. Vă va ajuta să oferiți aplicației dvs. un aspect mai nativ. Pur și simplu oferă aplicației un aspect și o structură fără date - un ecran de tranziție care are scopul de a îmbunătăți experiența de încărcare a aplicației dvs. web.

Iată câteva exemple de shell-uri de aplicații din aplicații native:

Google Inbox App Shell
Google Inbox App Shell: cu câteva milisecunde înainte ca e-mailurile să se încarce în shell-ul aplicației. (Previzualizare mare)
App Shell nativ Twitter
Aplicația nativă Twitter pe Android: shell-ul aplicației care arată bara de navigare, file și încărcător. (Previzualizare mare)

Și iată exemple de shell-uri de aplicații de la PWA:

App Shell al PWA al Twitter
Învelișul aplicației PWA Twitter (previzualizare mare)
Shell de aplicație a PWA de la Flipkart
Shell-ul aplicației PWA al lui Flipkart (previzualizare mare)

Utilizatorilor le place experiența de încărcare a shell-urilor de aplicații, deoarece disprețuiesc ecranele goale. Un ecran gol îl face pe utilizator să simtă că site-ul web nu se încarcă. Îi face să se simtă ca și cum site-ul ar fi blocat.

shell-urile aplicației încearcă să picteze structura de navigare a aplicației cât mai curând posibil, cum ar fi bara de navigare, bara de file, precum și un încărcător care înseamnă că conținutul pe care l-ați solicitat este încărcat.

Deci, cum construiți o aplicație Shell?

Modelul shell-ului aplicației acordă prioritate încărcării HTML, CSS și JavaScript care vor fi randate primul. Aceasta înseamnă că trebuie să acordăm acestor resurse prioritate deplină, prin urmare trebuie să includeți acele active. Deci, pentru a construi un shell de aplicație, trebuie pur și simplu să introduceți HTML, CSS și JavaScript care sunt responsabile pentru shell-ul aplicației. Desigur, nu ar trebui să includeți totul, ci mai degrabă să rămâneți într-o limită de aproximativ 30 până la 40 KB în total.

Puteți vedea shell-ul aplicației încorporate în index.html . Puteți inspecta codul sursă verificând fișierul index.html și îl puteți previzualiza în browser ștergând elementul <main> din instrumentele de dezvoltare:

Monede PWA App Shell cu bară de navigare și încărcător
Aplicația Shell pe care o construim în acest articol. (Previzualizare mare)

Funcționează offline?

Să simulăm deconectarea! Deschideți DevTools, navigați la fila de rețea și bifați caseta de selectare „Offline”. Când reîncărcați pagina, veți vedea că vom primi pagina offline a browserului.

Pagina de eroare offline a browserului
Solicitarea către pagina de pornire a eșuat, așa că, evident, vedem acest lucru ca urmare. (Previzualizare mare)

Asta pentru că solicitarea inițială către / (care va încărca fișierul index.html ) va eșua deoarece Internetul este offline. Singura modalitate prin care ne putem recupera din acel eșec al cererii este să avem un lucrător de service.

Să vizualizăm cererea fără un lucrător de service:

Animarea unei solicitări de rețea care trece de la browserul clientului la internet.
Solicitarea rețelei trece de la browser la Internet și înapoi. (Icoane de la flaticon.com) (Previzualizare mare)

Un lucrător de servicii este un proxy de rețea programabil, ceea ce înseamnă că se află între pagina dvs. web și Internet. Acest lucru vă permite să controlați solicitările de rețea de intrare și de ieșire.

Animarea unei cereri de rețea interceptată de lucrătorul de serviciu.
Solicitarea rețelei este interceptată de către lucrătorul de servicii. (Icoane de la flaticon.com) (Previzualizare mare)

Acest lucru este benefic deoarece acum putem redirecționa această solicitare eșuată în cache (presupunând că avem conținutul în cache).

Animație a unei solicitări de rețea interceptată de lucrătorul serviciului și redirecționată către cache.
Solicitarea de rețea este redirecționată în cache atunci când există deja în cache. (Icoane de la flaticon.com) (Previzualizare mare)

Un lucrător de servicii este, de asemenea, un tip de lucrător web, ceea ce înseamnă că rulează separat de pagina principală și nu are acces nici la window , nici la obiectul document .

Precacheați aplicația Shell

Pentru ca aplicația noastră să funcționeze offline, vom începe prin a precache shell-ul aplicației.

Deci, să începem prin a instala pluginul Webpack Workbox:

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

Apoi vom deschide fișierul nostru index.js și vom înregistra lucrătorul de service:

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

Apoi, deschideți fișierul webpack.config.js și haideți să configuram pluginul Workbox webpack:

 //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" }) ]

Acest lucru va instrui Workbox să folosească fișierul nostru ./src/src-sw.js ca bază. Fișierul generat se va numi sw.js și va fi în folderul dist .

Apoi creați un fișier ./src/src-sw.js la nivelul rădăcină și scrieți următoarele în interiorul acestuia:

 workbox.precaching.precacheAndRoute(self.__precacheManifest);

Notă : variabila self.__precacheManifest va fi importată dintr-un fișier care va fi generat dinamic de workbox.

Acum sunteți gata să vă construiți codul cu npm run build și Workbox va genera două fișiere în folderul dist :

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

sw.js importă caseta de lucru din CDN, precum și precache-manifest.[chunkhash].js .

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

Manifestul precache listează numele fișierelor care au fost procesate de webpack și care ajung în folderul dist . Vom folosi aceste fișiere pentru a le precache în browser. Aceasta înseamnă că atunci când site-ul dvs. se încarcă pentru prima dată și înregistrează lucrătorul de service, acesta va stoca în cache aceste active, astfel încât să poată fi utilizate data viitoare.

De asemenea, puteți observa că unele intrări au o „revizuire”, în timp ce altele nu. Asta pentru că revizuirea poate fi uneori dedusă din chunkhash din numele fișierului. De exemplu, să aruncăm o privire mai atentă la numele fișierului main.49467c51ac5e0cb2b58e.js . Are o revizuire în numele fișierului, care este chunkhash 49467c51ac5e0cb2b58e .

Acest lucru permite Workbox să înțeleagă când se modifică fișierele dvs., astfel încât să curețe sau să actualizeze doar fișierele care au fost modificate, în loc să arunce tot memoria cache de fiecare dată când publicați o nouă versiune a lucrătorului de servicii.

Prima dată când încărcați pagina, lucrătorul de service se va instala. Puteți vedea asta în DevTools. În primul rând, este solicitat fișierul sw.js , care apoi solicită toate celelalte fișiere. Sunt marcate clar cu pictograma roată.

Captură de ecran a filei DevTools Network atunci când lucrătorul de service este instalat.
Solicitările marcate cu pictograma ️ sunt solicitări inițiate de lucrătorul serviciului. (Previzualizare mare)

Deci Workbox se va inițializa și va precache toate fișierele care sunt în precache-manifest. Este important să verificați din nou dacă nu aveți fișiere inutile în fișierul precache-manifest , cum ar fi fișierele .map sau fișierele care nu fac parte din shell-ul aplicației.

În fila de rețea, putem vedea cererile venite de la lucrătorul de service. Și acum, dacă încercați să mergeți offline, shell-ul aplicației este deja precacheat, așa că funcționează chiar dacă suntem offline!

Captură de ecran a filei Dev Tools Network care arată apelurile API care nu au reușit.
Apelurile API eșuează atunci când mergem offline. (Previzualizare mare)

Cache Rute dinamice

Ați observat că atunci când am fost offline, shell-ul aplicației funcționează, dar nu și datele noastre? Acest lucru se datorează faptului că aceste apeluri API nu fac parte din shell-ul aplicației precache . Când nu există conexiune la internet, aceste solicitări vor eșua și utilizatorul nu va putea vedea informațiile despre monedă.

Cu toate acestea, aceste solicitări nu pot fi precacheate deoarece valoarea lor provine dintr-un API. Mai mult, atunci când începeți să aveți mai multe pagini, nu doriți să stocați în cache toate solicitările API dintr-o singură mișcare. În schimb, doriți să le păstrați în cache atunci când utilizatorul vizitează pagina respectivă.

Acestea le numim „date dinamice”. Acestea includ adesea apeluri API, precum și imagini și alte active care sunt solicitate atunci când un utilizator face o anumită acțiune pe site-ul dvs. (de exemplu, când navighează la o pagină nouă).

Le puteți stoca în cache folosind modulul de rutare al Workbox. Iată cum:

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

Aceasta va configura memoria cache dinamică pentru orice adresă URL de solicitare care se potrivește cu adresa URL https://api.exchangeratesapi.io/latest .

Strategia de stocare în cache pe care am folosit-o aici se numește NetworkFirst ; mai sunt două care sunt adesea folosite:

  1. CacheFirst
  2. StaleWhileRevalidate

CacheFirst va căuta mai întâi în cache. Dacă nu este găsit, atunci îl va primi din rețea. StaleWhileRevalidate va merge în rețea și în cache în același timp. Întoarceți răspunsul cache-ului la pagină (în timp ce se află în fundal), acesta va folosi noul răspuns de rețea pentru a actualiza memoria cache pentru următoarea utilizare.

Pentru cazul nostru de utilizare, a trebuit să mergem cu NetworkFirst , deoarece avem de-a face cu rate valutare care se schimbă foarte des. Cu toate acestea, atunci când utilizatorul este offline, îi putem arăta cel puțin tarifele așa cum erau acum 10 minute - de aceea am folosit pluginul de expirare cu maxAgeSeconds setat la 10 * 60 secunde.

Gestionați actualizările aplicației

De fiecare dată când un utilizator vă încarcă pagina, browserul va rula codul navigator.serviceWorker.register chiar dacă lucrătorul de service este deja instalat și rulează. Acest lucru permite browserului să detecteze dacă există o nouă versiune a lucrătorului de servicii. Când browserul observă că fișierul nu s-a schimbat, omite doar apelul de înregistrare. Odată ce fișierul se modifică, browserul înțelege că există o nouă versiune a service worker, astfel că instalează noul service worker paralel cu service worker-ul care rulează în prezent .

Cu toate acestea, se întrerupe în faza de installed/waiting deoarece doar un singur lucrător de service poate fi activat în același timp.

Ciclul de viață al unui lucrător de service: analizat, instalat/în așteptare, activat și redundant
Un ciclu de viață simplificat al unui lucrător în service (previzualizare mare)

Numai când toate ferestrele browserului controlate de către lucrătorul de service anterior sunt închise, atunci devine sigur ca noul lucrător de service să se activeze.

De asemenea, puteți controla asta manual apelând skipWaiting() (sau self.skipWaiting() deoarece self este contextul global de execuție în serviciul de lucru). Cu toate acestea, de cele mai multe ori ar trebui să faceți asta numai după ce întrebați utilizatorul dacă dorește să obțină cea mai recentă actualizare.

Din fericire, workbox-window ne ajută să realizăm acest lucru. Este o nouă bibliotecă de ferestre introdusă în Workbox v4, care are ca scop simplificarea sarcinilor comune din partea ferestrei.

Să începem prin a-l instala cu următoarele:

 npm install workbox-window

Apoi, importați Workbox în partea de sus a fișierului index.js :

 import { Workbox } from "workbox-window";

Apoi vom înlocui codul nostru de înregistrare cu următorul:

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

Vom găsi apoi butonul de actualizare care are ID-ul actualizare a aplicației și ascultați evenimentul workbox-waiting :

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

Acest cod va afișa butonul de actualizare atunci când există o nouă actualizare (deci atunci când lucrătorul de service este într-o stare de așteptare) și va trimite un mesaj SKIP_WAITING lucrătorului de service.

Va trebui să actualizăm fișierul de serviciu și să gestionăm evenimentul SKIP_WAITING astfel încât să skipWaiting :

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

Acum rulați npm run dev apoi reîncărcați pagina. Accesați codul și actualizați titlul barei de navigare la „Navbar v2”. Reîncărcați pagina din nou și ar trebui să puteți vedea pictograma de actualizare.

Încheierea

Site-ul nostru web funcționează acum offline și este capabil să informeze utilizatorul despre noile actualizări. Vă rugăm să rețineți, totuși, că cel mai important factor atunci când construiți un PWA este experiența utilizatorului. Concentrați-vă întotdeauna pe construirea de experiențe care sunt ușor de utilizat de către utilizatori. Noi, ca dezvoltatori, avem tendința să fim prea entuziasmați de tehnologie și ajungem adesea să uităm de utilizatorii noștri.

Dacă doriți să faceți acest lucru cu un pas mai departe, puteți adăuga un manifest al aplicației web care va permite utilizatorilor să adauge site-ul pe ecranul lor de pornire. Și dacă doriți să aflați mai multe despre Workbox, puteți găsi documentația oficială pe site-ul web Workbox.

Citiți suplimentare despre SmashingMag:

  • Puteți câștiga mai mulți bani cu o aplicație mobilă sau cu un PWA?
  • Un ghid extins pentru aplicații web progresive
  • Nativ și PWA: alegeri, nu provocatori!
  • Construirea unui PWA folosind Angular 6