Zbuduj PWA z Webpackiem i Workbox
Opublikowany: 2022-03-10Progresywna aplikacja internetowa (PWA) to witryna, która wykorzystuje nowoczesną technologię do dostarczania w Internecie podobnych do aplikacji doświadczeń. Jest to ogólny termin dla nowych technologii, takich jak „manifest aplikacji internetowej”, „service worker” i nie tylko. Po połączeniu te technologie umożliwiają dostarczanie szybkich i angażujących doświadczeń użytkowników w Twojej witrynie.
Ten artykuł to samouczek krok po kroku dotyczący dodawania pracownika usługi do istniejącej jednostronicowej witryny sieci Web. Service Worker pozwoli Ci sprawić, że Twoja witryna będzie działać w trybie offline, jednocześnie powiadamiając użytkowników o aktualizacjach witryny. Pamiętaj, że jest to oparte na małym projekcie, który jest dołączony do Webpacka, więc będziemy używać wtyczki Workbox Webpack (Workbox v4).
Zalecanym podejściem jest użycie narzędzia do generowania pracownika usługi, ponieważ pozwala ono efektywnie zarządzać pamięcią podręczną. Będziemy używać Workbox — zestawu bibliotek, które ułatwiają generowanie kodu Service Worker — do generowania naszego Service Worker w tym samouczku.
W zależności od projektu możesz korzystać z Workboksa na trzy różne sposoby:
- Dostępny jest interfejs wiersza poleceń , który pozwala zintegrować workbox z dowolną posiadaną aplikacją;
- Dostępny jest moduł Node.js, który pozwala zintegrować workbox z dowolnym narzędziem do budowania Node, takim jak gulp lub grunt;
- Dostępna jest wtyczka webpack , która umożliwia łatwą integrację z projektem zbudowanym za pomocą Webpack.
Webpack to pakiet modułów. Upraszczając, możesz myśleć o tym jako o narzędziu, które zarządza twoimi zależnościami JavaScript. Umożliwia importowanie kodu JavaScript z bibliotek i łączenie kodu JavaScript w jeden lub wiele plików.
Aby rozpocząć, sklonuj następujące repozytorium na swoim komputerze:
git clone [email protected]:jadjoubran/workbox-tutorial-v4.git cd workbox-tutorial-v4 npm install npm run dev
Następnie przejdź do https://localhost:8080/
. Powinieneś być w stanie zobaczyć aplikację walutową, której będziemy używać w tym samouczku:
Zacznij od powłoki aplikacji
Powłoka aplikacji (lub „powłoka aplikacji”) to wzorzec inspirowany aplikacjami natywnymi. Pomoże to nadać aplikacji bardziej natywny wygląd. Po prostu zapewnia aplikacji układ i strukturę bez żadnych danych — ekran przejściowy, który ma na celu usprawnienie ładowania aplikacji internetowej.
Oto kilka przykładów powłok aplikacji z aplikacji natywnych:
A oto przykłady powłok aplikacji z PWA:
Użytkownicy lubią ładowanie powłok aplikacji, ponieważ gardzą pustymi ekranami. Pusty ekran sprawia, że użytkownik czuje, że strona się nie ładuje. To sprawia, że czują się tak, jakby strona utknęła.
Powłoki aplikacji próbują narysować strukturę nawigacyjną aplikacji tak szybko, jak to możliwe, na przykład pasek nawigacyjny, pasek kart oraz moduł ładujący, który oznacza, że żądana zawartość jest ładowana.
Jak więc zbudować powłokę aplikacji?
Wzorzec powłoki aplikacji nadaje priorytet ładowaniu kodu HTML, CSS i JavaScript, który zostanie wyrenderowany jako pierwszy. Oznacza to, że musimy nadać tym zasobom pełny priorytet, dlatego musisz je wbudować. Aby więc zbudować powłokę aplikacji, wystarczy wbudować kod HTML, CSS i JavaScript, które są odpowiedzialne za powłokę aplikacji. Oczywiście nie powinieneś wszystkiego inline, ale raczej trzymaj się limitu około 30 do 40 KB.
Możesz zobaczyć wbudowaną powłokę aplikacji w index.html . Możesz sprawdzić kod źródłowy, sprawdzając plik index.html , i możesz wyświetlić podgląd w przeglądarce, usuwając element <main>
w narzędziach programistycznych:
Czy to działa w trybie offline?
Zasymulujmy przejście do trybu offline! Otwórz DevTools, przejdź do zakładki sieci i zaznacz pole wyboru „Offline”. Po ponownym załadowaniu strony zobaczysz, że otrzymamy stronę offline przeglądarki.
Dzieje się tak, ponieważ początkowe żądanie do /
(które załaduje plik index.html ) zakończy się niepowodzeniem, ponieważ Internet jest w trybie offline. Jedynym sposobem na naprawienie tego niepowodzenia żądania jest zatrudnienie pracownika serwisu.
Zwizualizujmy żądanie bez pracownika serwisu:
Service worker to programowalny serwer proxy sieci, co oznacza, że znajduje się pomiędzy Twoją stroną internetową a Internetem. Pozwala to kontrolować przychodzące i wychodzące żądania sieciowe.
Jest to korzystne, ponieważ możemy teraz przekierować to nieudane żądanie do pamięci podręcznej (zakładając, że mamy zawartość w pamięci podręcznej).
Service Worker jest również rodzajem Web Worker, co oznacza, że działa niezależnie od strony głównej i nie ma dostępu ani do window
ani do obiektu document
.
Przygotuj powłokę aplikacji w pamięci podręcznej
Aby nasza aplikacja działała w trybie offline, zaczniemy od wstępnego buforowania powłoki aplikacji.
Zacznijmy więc od zainstalowania wtyczki Webpack Workbox:
npm install --save-dev workbox-webpack-plugin
Następnie otworzymy nasz plik index.js i zarejestrujemy service worker:
if ("serviceWorker" in navigator){ window.addEventListener("load", () => { navigator.serviceWorker.register("/sw.js"); }) }
Następnie otwórz plik webpack.config.js i skonfigurujmy wtyczkę webpack 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" }) ]
To poinstruuje Workboksa, aby użył naszego pliku ./src/src-sw.js jako bazy. Wygenerowany plik będzie nazywał się sw.js i będzie znajdował się w folderze dist
.
Następnie utwórz plik ./src/src-sw.js na poziomie głównym i zapisz w nim:
workbox.precaching.precacheAndRoute(self.__precacheManifest);
Uwaga : Zmienna self.__precacheManifest
zostanie zaimportowana z pliku, który będzie dynamicznie generowany przez workbox.
Teraz jesteś gotowy do zbudowania kodu za pomocą npm run build
, a Workbox wygeneruje dwa pliki w folderze dist
:
- precache-manifest.66cf63077c7e4a70ba741ee9e6a8da29.js
- sw.js
sw.js importuje workbox z CDN, a także precache-manifest.[chunkhash].js .
//precache-manifest.[chunkhash].js file self.__precacheManifest = (self.__precacheManifest || []).concat([ "revision": "ba8f7488757693a5a5b1e712ac29cc28", "url": "index.html" }, "url": "main.49467c51ac5e0cb2b58e.js" ]);
Manifest pamięci podręcznej zawiera nazwy plików, które zostały przetworzone przez pakiet webpack i trafiły do folderu dist
. Wykorzystamy te pliki do wstępnego buforowania ich w przeglądarce. Oznacza to, że gdy Twoja witryna wczytuje się po raz pierwszy i zarejestruje Service Workera, zbuforuje te zasoby, aby można było ich użyć następnym razem.
Możesz również zauważyć, że niektóre wpisy mają „poprawkę”, podczas gdy inne nie. Dzieje się tak, ponieważ wersję można czasem wywnioskować z fragmentu nazwy pliku. Na przykład przyjrzyjmy się bliżej nazwie pliku main.49467c51ac5e0cb2b58e.js . Jego wersja znajduje się w nazwie pliku, czyli chunkhash 49467c51ac5e0cb2b58e .
Dzięki temu Workbox rozumie, kiedy Twoje pliki się zmieniają, dzięki czemu czyści lub aktualizuje tylko te pliki, które zostały zmienione, zamiast zrzucać całą pamięć podręczną za każdym razem, gdy publikujesz nową wersję swojego Service Workera.
Przy pierwszym załadowaniu strony, Service Worker zainstaluje. Widać to w DevTools. Najpierw żądany jest plik sw.js , który następnie żąda wszystkich pozostałych plików. Są wyraźnie oznaczone ikoną koła zębatego.
Tak więc Workbox zainicjuje i wstępnie buforuje wszystkie pliki, które znajdują się w precache-manifest. Ważne jest, aby dokładnie sprawdzić, czy w pliku manifestu wstępnego pamięci podręcznej nie ma żadnych niepotrzebnych plików, takich jak pliki .map lub pliki, które nie są częścią powłoki aplikacji.
W zakładce sieci możemy zobaczyć żądania przychodzące od pracownika serwisu. A teraz, jeśli spróbujesz przejść do trybu offline, powłoka aplikacji jest już w pamięci podręcznej, więc działa nawet wtedy, gdy jesteśmy offline!
Pamięć podręczna tras dynamicznych
Czy zauważyłeś, że kiedy przeszliśmy do trybu offline, powłoka aplikacji działa, ale nie nasze dane? Dzieje się tak, ponieważ te wywołania interfejsu API nie są częścią wstępnie buforowanej powłoki aplikacji . Gdy nie ma połączenia z Internetem, żądania te zakończą się niepowodzeniem, a użytkownik nie będzie mógł zobaczyć informacji o walucie.
Jednak tych żądań nie można wstępnie buforować, ponieważ ich wartość pochodzi z interfejsu API. Co więcej, gdy zaczynasz mieć wiele stron, nie chcesz buforować wszystkich żądań API za jednym razem. Zamiast tego chcesz je buforować, gdy użytkownik odwiedza tę stronę.
Nazywamy to „danymi dynamicznymi”. Często zawierają one wywołania API, a także obrazy i inne zasoby, które są wymagane, gdy użytkownik wykonuje określoną czynność w Twojej witrynie (np. gdy przegląda nową stronę).
Możesz je buforować za pomocą modułu routingu Workboksa. Oto jak:
//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 }) ] }) );
Spowoduje to skonfigurowanie dynamicznego buforowania dla dowolnego adresu URL żądania, który pasuje do adresu URL https://api.exchangeratesapi.io/latest
.
Strategia buforowania, której tutaj użyliśmy, nazywa się NetworkFirst
; są dwa inne, które są często używane:
-
CacheFirst
-
StaleWhileRevalidate
CacheFirst
najpierw poszuka go w pamięci podręcznej. Jeśli nie zostanie znaleziony, pobierze go z sieci. StaleWhileRevalidate
przejdzie do sieci i pamięci podręcznej w tym samym czasie. Zwróć odpowiedź pamięci podręcznej na stronę (w tle), zostanie użyta nowa odpowiedź sieciowa do zaktualizowania pamięci podręcznej przy następnym użyciu.
W naszym przypadku użycia musieliśmy wybrać NetworkFirst
, ponieważ mamy do czynienia z kursami walut, które zmieniają się bardzo często. Jednak gdy użytkownik przejdzie w tryb offline, możemy przynajmniej pokazać mu stawki takie, jakie były 10 minut temu — dlatego użyliśmy wtyczki wygaśnięcia z wartością maxAgeSeconds
ustawioną na 10 * 60
sekund.
Zarządzaj aktualizacjami aplikacji
Za każdym razem, gdy użytkownik ładuje twoją stronę, przeglądarka uruchomi kod navigator.serviceWorker.register
, nawet jeśli Service Worker jest już zainstalowany i uruchomiony. Dzięki temu przeglądarka może wykryć, czy istnieje nowa wersja Service Workera. Gdy przeglądarka zauważy, że plik się nie zmienił, po prostu pomija wywołanie rejestracji. Po zmianie tego pliku przeglądarka rozpoznaje, że istnieje nowa wersja Service Worker, dlatego instaluje nowy Service Worker równolegle do aktualnie działającego Service Worker .
Jednak zatrzymuje się na etapie installed/waiting
, ponieważ tylko jeden pracownik serwisu może być aktywowany w tym samym czasie.
Dopiero gdy wszystkie okna przeglądarki kontrolowane przez poprzedniego pracownika serwisu zostaną zamknięte, aktywacja nowego pracownika serwisu staje się bezpieczna.
Można to również ręcznie kontrolować, wywołując skipWaiting()
(lub self.skipWaiting()
, ponieważ self
jest globalnym kontekstem wykonania w procesie roboczym). Jednak w większości przypadków powinieneś to zrobić dopiero po zapytaniu użytkownika, czy chce pobrać najnowszą aktualizację.
Na szczęście workbox-window
pomaga nam to osiągnąć. Jest to nowa biblioteka okien wprowadzona w Workboksie v4, która ma na celu uproszczenie typowych zadań po stronie okna.
Zacznijmy od zainstalowania go z następującymi elementami:
npm install workbox-window
Następnie zaimportuj Workbox
na górze pliku index.js :
import { Workbox } from "workbox-window";
Następnie zastąpimy nasz kod rejestracyjny następującym:
if ("serviceWorker" in navigator) { window.addEventListener("load", () => { const wb = new Workbox("/sw.js"); wb.register(); }); }
Następnie znajdziemy przycisk aktualizacji z identyfikatoremworkbox-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" }); }); });
Ten kod pokaże przycisk aktualizacji, gdy pojawi się nowa aktualizacja (czyli gdy pracownik serwisu jest w stanie oczekiwania) i wyśle wiadomość SKIP_WAITING
do pracownika serwisu.
Musimy zaktualizować plik Service Worker i obsłużyć zdarzenie SKIP_WAITING
tak, aby skipWaiting
:
//add in src-sw.js addEventListener("message", event => { if (event.data && event.data.type === "SKIP_WAITING") { skipWaiting(); });
Teraz uruchom npm run dev
a następnie ponownie załaduj stronę. Wejdź do kodu i zaktualizuj tytuł paska nawigacyjnego do „Navbar v2”. Ponownie załaduj stronę i powinieneś zobaczyć ikonę aktualizacji.
Zawijanie
Nasza strona internetowa działa teraz w trybie offline i jest w stanie poinformować użytkownika o nowych aktualizacjach. Pamiętaj jednak, że najważniejszym czynnikiem podczas budowania PWA jest doświadczenie użytkownika. Zawsze skupiaj się na budowaniu doświadczeń, które są łatwe w użyciu przez użytkowników. Jako programiści jesteśmy zbyt podekscytowani technologią i często zapominamy o naszych użytkownikach.
Jeśli chcesz pójść o krok dalej, możesz dodać manifest aplikacji internetowej, który umożliwi użytkownikom dodanie witryny do ekranu głównego. A jeśli chcesz dowiedzieć się więcej o Workboksie, możesz znaleźć oficjalną dokumentację na stronie Workbox.
Dalsze czytanie na SmashingMag:
- Czy możesz zarobić więcej pieniędzy dzięki aplikacji mobilnej lub PWA?
- Obszerny przewodnik po progresywnych aplikacjach internetowych
- Native i PWA: wybory, a nie pretendenci!
- Budowanie PWA przy użyciu Angular 6