Przejdź do treści

Tech Website

Porady i Wiedza ze Świata Technologii

Progressive Web Apps – czym są i jak zbudować własną aplikację PWA?

13 min czytania
Progressive Web Apps – czym są i jak zbudować własną aplikację PWA?

Powszechne przekonanie jest takie, że żeby dać użytkownikom mobilnym pełnowartościową aplikację, trzeba wydać osobny budżet na wersję iOS i osobną na Androida, a potem jeszcze przejść przez weryfikację w sklepach. W praktyce jednak dla dużej grupy projektów – sklepów internetowych, serwisów informacyjnych, platform contentowych – istnieje inna droga, która nie wymaga ani dwóch osobnych baz kodu, ani obecności w App Store.

Progressive Web Apps to aplikacje webowe, które można zainstalować na ekranie głównym smartfona, uruchamiać bez połączenia z internetem i z których można otrzymywać powiadomienia push – bez pobierania czegokolwiek z Google Play czy App Store. Przeglądarka traktuje je podobnie jak aplikacje natywne: po dodaniu do ekranu głównego uruchamiają się bez paska adresu, ze splash screenem i własną ikoną. Aktualizacje trafiają do użytkownika automatycznie przy każdym wejściu na stronę – bez zatwierdzeń, bez czekania na moderację sklepu.

Nazwa „Progressive” nie jest przypadkowa. Na starszej przeglądarce aplikacja PWA zachowuje się jak zwykła strona internetowa. Na nowszej – tej samej stronie dokłada tryb offline, możliwość instalacji i powiadomienia push. To podejście sprawia, że nie trzeba rezygnować z żadnej grupy użytkowników, żeby skorzystać z nowych możliwości.

Poniżej znajdziesz wszystko, czego potrzebujesz, żeby zrozumieć, jak aplikacja PWA działa od środka, kiedy warto ją wybrać zamiast aplikacji natywnej, jak wdrożyć ją na WordPressie i jak zmierzyć, czy konfiguracja jest poprawna.

Jak działa PWA od środka – Service Worker i manifest

Dwa pliki decydują o tym, czy przeglądarka zakwalifikuje stronę jako aplikację PWA i zaoferuje jej instalację: Service Worker oraz manifest aplikacji. Bez żadnego z nich strona pozostaje zwykłą stroną – nawet jeśli wygląda jak aplikacja.

Service Worker to skrypt JavaScript rejestrowany przez przeglądarkę i działający w tle, niezależnie od otwartej karty. Jego główna rola polega na przechwytywaniu żądań sieciowych: zamiast odpytywać serwer przy każdym zasobie, może serwować pliki bezpośrednio z lokalnego cache. Dzięki temu strona ładuje się nawet wtedy, gdy urządzenie jest offline lub ma niestabilne połączenie.

Service Worker obsługuje też synchronizację w tle. Jeśli użytkownik doda produkt do koszyka bez połączenia z internetem, skrypt zapisze tę akcję lokalnie i wykona ją automatycznie po odzyskaniu sieci. To samo dotyczy powiadomień push – mogą docierać do użytkownika nawet przy zamkniętej przeglądarce, bo Service Worker działa niezależnie od interfejsu strony.

Ważne ograniczenie techniczne: Service Worker działa wyłącznie przez HTTPS. Certyfikat SSL nie jest opcją – to warunek konieczny, bez którego przeglądarka w ogóle nie zarejestruje skryptu.

Przykład minimalnego kodu rejestracji Service Workera wygląda następująco:

„`javascript

if (’serviceWorker’ in navigator) {

navigator.serviceWorker.register(’/sw.js’)

.then(reg => console.log(’SW zarejestrowany:’, reg.scope))

.catch(err => console.error(’Błąd rejestracji SW:’, err));

}

„`

Sam plik `sw.js` definiuje, które zasoby trafiają do cache i jaką strategię stosuje przy żądaniach. Dwie podstawowe strategie to Cache First – najpierw szukaj w cache, odpytuj serwer tylko gdy zasobu nie ma lokalnie (sprawdza się przy plikach CSS, JS i obrazach, które zmieniają się rzadko) – oraz Network First, gdzie skrypt najpierw próbuje pobrać zasób z sieci, a cache traktuje jako rezerwę przy braku połączenia. Dla sklepów opartych na WooCommerce zalecane podejście to Network First dla stron produktów (żeby użytkownik widział aktualne ceny i stany magazynowe) i Cache First dla zasobów statycznych.

Manifest aplikacji to osobny plik w formacie JSON. Zawiera nazwę aplikacji, skróconą nazwę wyświetlaną pod ikoną, zestaw ikon w różnych rozmiarach, kolor motywu paska systemowego oraz tryb wyświetlania. Manifest decyduje o tym, czy aplikacja startuje ze splash screenem i czy uruchamia się pełnoekranowo bez paska przeglądarki. Minimalny poprawny manifest wygląda tak:

„`json

{

„name”: „Moja aplikacja PWA”,

„short_name”: „MojaApp”,

„start_url”: „/”,

„display”: „standalone”,

„background_color”: „#ffffff”,

„theme_color”: „#1a73e8”,

„icons”: [

{

„src”: „/icons/icon-192.png”,

„sizes”: „192×192”,

„type”: „image/png”

},

{

„src”: „/icons/icon-512.png”,

„sizes”: „512×512”,

„type”: „image/png”

}

]

}

„`

Ikony muszą mieć co najmniej dwa rozmiary: 192×192 px i 512×512 px, w formacie PNG z przezroczystym tłem. Mniejsze lub brakujące ikony to jeden z najczęstszych powodów, dla których Lighthouse zgłasza błąd instalacji PWA. Plik manifestu należy podlinkować w sekcji `` strony tagiem ``.

PWA czy aplikacja natywna – kiedy wybrać co?

Odpowiedź zależy od tego, czego projekt faktycznie potrzebuje od warstwy sprzętowej i od kanałów dystrybucji.

Aplikacja PWA działa jako jeden projekt dla wszystkich platform. Dystrybuuje się ją linkiem URL – użytkownik nie musi nic pobierać, żeby skorzystać z pełnej funkcjonalności. Aktualizacje są natychmiastowe i automatyczne, bez przechodzenia przez weryfikację sklepu. To istotna przewaga przy projektach, gdzie szybkość wdrożenia zmian ma znaczenie – sklep może zaktualizować cennik lub interfejs w ciągu minut, a nie dni.

Ograniczeniem PWA jest dostęp do sprzętu. Kamera i GPS są dostępne, ale już Bluetooth, NFC i czytniki biometryczne wymagają głębszej integracji, którą zapewnia tylko kod natywny. Projekt Fugu prowadzony przez Google stopniowo rozszerza te możliwości – aplikacje PWA zyskują dostęp do systemu plików, Bluetooth i NFC – ale wsparcie przeglądarek dla tych API jest wciąż nierównomierne.

Aplikacja natywna wymaga osobnych projektów dla iOS i Androida, co oznacza dwa osobne zespoły lub framework wieloplatformowy. Dystrybucja przez App Store i Google Play daje widoczność w rankingach i recenzjach – to ważny kanał pozyskiwania użytkowników dla aplikacji, których model wzrostu opiera się na odkrywalności w sklepie. Aktualizacje przechodzą przez moderację, co może opóźniać wdrożenie poprawek bezpieczeństwa nawet o kilka dni.

PWA sprawdza się najlepiej w e-commerce, mediach i serwisach informacyjnych, gdzie liczy się szybkość ładowania, tryb offline i powiadomienia push, a nie głęboka integracja ze sprzętem. Aplikacja natywna ma przewagę przy grach mobilnych, aplikacjach fitness z monitoringiem zdrowia przez czujniki urządzenia i wszędzie tam, gdzie widoczność w App Store jest głównym kanałem pozyskiwania nowych użytkowników.

Koszt jest często argumentem decydującym. Dedykowana aplikacja PWA budowana od zera kosztuje zwykle 30 000–150 000 zł, podczas gdy dwie osobne aplikacje natywne (iOS + Android) to budżet wyższy przeciętnie o 40–60%.

Jak zbudować PWA krok po kroku?

Budowa Progressive Web App nie wymaga egzotycznych narzędzi. Wystarczy edytor kodu, przeglądarka z DevTools i serwer HTTPS – lokalnie zastępuje go `localhost`. Cały proces można podzielić na cztery etapy, które razem dają działającą aplikację gotową do audytu Lighthouse.

Punkt wyjścia: projekt i struktura plików

Zanim napiszesz pierwszą linię kodu, zdecyduj, co aplikacja ma robić offline. To pytanie kształtuje całą architekturę cache’owania. Jeśli planujesz sklep, offline powinno działać przeglądanie wcześniej odwiedzonych produktów i koszyk. Jeśli budujesz serwis informacyjny – ostatnie artykuły i strona główna. Brak tej decyzji na początku oznacza przepisywanie Service Workera w połowie projektu.

Minimalna struktura pliku to: `index.html`, `manifest.json`, `sw.js` (plik Service Workera) i katalog z ikonami. Do tego dochodzi logika aplikacji – JavaScript, CSS i zasoby statyczne. Warto od razu przyjąć konwencję wersjonowania pliku Service Workera w komentarzu na jego początku, np. `// v1.0.0`. Zmiana tej wartości przy każdym wdrożeniu pozwala wymusić odświeżenie cache u użytkowników.

Rejestracja Service Workera i pierwsza strategia cache

Rejestrację umieszcza się w głównym pliku JavaScript lub bezpośrednio w `index.html`, w bloku warunkowym sprawdzającym wsparcie przeglądarki:

„`js

if (’serviceWorker’ in navigator) {

navigator.serviceWorker.register(’/sw.js’)

.then(reg => console.log(’SW zarejestrowany:’, reg.scope))

.catch(err => console.error(’Błąd rejestracji SW:’, err));

}

„`

Wewnątrz `sw.js` definiujesz trzy zdarzenia: `install`, `activate` i `fetch`. Podczas `install` preładujesz zasoby krytyczne – HTML, CSS i JavaScript aplikacji – do pamięci cache. Podczas `activate` usuwasz stare wersje cache, żeby nie zajmowały miejsca na urządzeniu. Zdarzenie `fetch` przechwytuje każde żądanie sieciowe i decyduje, czy odpowiedź ma przyjść z cache, z sieci, czy z połączenia obu.

Najprostsza strategia na start to Cache First: Service Worker najpierw sprawdza cache, a sieć odpytuje tylko wtedy, gdy zasobu tam nie ma. Działa dobrze dla zasobów statycznych, które rzadko się zmieniają – czcionek, ikon, arkuszy stylów. Dla danych dynamicznych, jak lista produktów czy wyniki wyszukiwania, lepsza jest strategia Network First: aplikacja próbuje pobrać świeże dane z sieci, a cache traktuje jako zapasowe źródło przy braku połączenia.

Narzędzia, które skracają pracę

Pisanie Service Workera od zera ma sens edukacyjnie, ale w produkcji większość projektów korzysta z biblioteki Workbox od Google. Workbox dostarcza gotowe strategie cache, obsługę wersjonowania i precachingu w kilku linijkach konfiguracji. Integruje się z Webpackiem, Vite i Rollupem przez dedykowane wtyczki – `workbox-webpack-plugin` lub `vite-plugin-pwa`.

Konfiguracja `vite-plugin-pwa` zajmuje zwykle 20–40 linii w pliku `vite.config.js` i automatycznie generuje zarówno manifest, jak i Service Workera na podstawie podanych parametrów. Dla projektów React, Vue lub Svelte to szybszy punkt startowy niż ręczna konfiguracja. Frameworki takie jak Next.js mają własne rozwiązania – `next-pwa` to wrapper Workboxa przystosowany do architektury Next.js z obsługą stron renderowanych po stronie serwera.

Jeśli zaczynasz od istniejącej strony opartej na WordPressie lub innym CMS, możesz dodać PWA przez wtyczkę. Super PWA i PWA for WP generują manifest i podstawowy Service Worker bez dotykania kodu źródłowego. To rozwiązanie nie daje pełnej kontroli nad strategiami cache, ale w wielu przypadkach wystarczy, żeby Lighthouse wystawił ocenę instalacji powyżej 90 punktów.

Audyt i testowanie – jak sprawdzić, czy PWA działa poprawnie?

Gotowy kod to połowa drogi. Zanim opublikujesz aplikację, musisz sprawdzić, czy spełnia wymagania PWA i czy tryb offline faktycznie działa tak, jak zaplanowałeś. Narzędzia do tego są wbudowane w przeglądarkę – nie potrzebujesz niczego instalować.

Poniżej lista obszarów, które warto zweryfikować przed publikacją:

  • manifest zawiera wymagane pola: `name`, `start_url`, `display` i co najmniej dwie ikony (192×192 px i 512×512 px);
  • Service Worker rejestruje się bez błędów i pojawia się w zakładce „Application” → „Service Workers” w DevTools;
  • strona działa w trybie offline – po zaznaczeniu opcji „Offline” w zakładce „Network” i przeładowaniu widać interfejs aplikacji, nie stronę błędu Chrome;
  • wszystkie zasoby krytyczne ładują się z cache (kolumna „Size” w DevTools pokazuje wpis „(ServiceWorker)”);
  • powiadomienia push działają – przycisk „Push” w sekcji „Service Workers” wywołuje testowe powiadomienie na urządzeniu;
  • audyt Lighthouse w kategorii „Progressive Web App” nie zgłasza błędów blokujących instalację.

Podstawowym narzędziem jest Lighthouse wbudowany w Chrome DevTools. Uruchom go z zakładki „Lighthouse”, zaznacz kategorię „Progressive Web App” i kliknij „Analyze page load”. Raport wskazuje konkretne braki: brakujący manifest, niepoprawne ikony, brak nagłówka HTTPS, niedziałający Service Worker. Każdy punkt ma opis i link do dokumentacji. Wynik 100 w kategorii PWA nie jest celem samym w sobie, ale lista błędów z raportu to gotowa lista zadań do poprawki.

Na urządzeniach mobilnych testuj instalację ręcznie. Na Androidzie Chrome wyświetla baner „Dodaj do ekranu głównego” po spełnieniu kryteriów PWA i co najmniej jednej wizycie użytkownika na stronie. Możesz wywołać ten baner ręcznie przez DevTools: w sekcji „Application” → „Manifest” kliknij „Add to homescreen”. Na iOS Safari instalacja odbywa się przez menu „Udostępnij” → „Dodaj do ekranu głównego” – automatyczny baner instalacyjny nie jest tam dostępny, co warto uwzględnić w projekcie onboardingu.

Wydajność mierz narzędziem WebPageTest lub PageSpeed Insights. Metryki, na które warto zwrócić uwagę, to Time to Interactive (TTI) poniżej 3,8 sekundy na połączeniu 4G oraz Largest Contentful Paint (LCP) poniżej 2,5 sekundy. Jeśli aplikacja nie mieści się w tych progach, sprawdź kolejność ładowania zasobów krytycznych i rozmiar pliku JavaScript – każde 100 kB dodatkowego JS to około 350 ms parsowania na przeciętnym urządzeniu mobilnym z 2022 roku.

Jak wdrożyć PWA na serwer i co po drodze może pójść nie tak?

Lokalne testy zakończone sukcesem nie gwarantują, że aplikacja zadziała poprawnie po publikacji. Kilka problemów pojawia się wyłącznie w środowisku produkcyjnym i może skutecznie zablokować instalację PWA.

Pierwsza kwestia to HTTPS. Service Worker rejestruje się wyłącznie na połączeniach szyfrowanych – jedynym wyjątkiem jest `localhost` podczas developmentu. Jeśli twój serwer obsługuje HTTP i HTTPS równolegle, upewnij się, że wszystkie żądania są przekierowywane na wersję szyfrowaną. Brak przekierowania 301 z HTTP na HTTPS sprawia, że część użytkowników trafi na niezabezpieczoną wersję strony i nie zobaczy opcji instalacji. Certyfikat od Let’s Encrypt wystarczy – jest bezpłatny i odnawiany automatycznie co 90 dni.

Drugi problem to nagłówki cache dla pliku Service Workera. Przeglądarka sprawdza aktualizacje SW co 24 godziny, ale jeśli serwer zwróci nagłówek `Cache-Control: max-age=31536000` dla pliku `sw.js`, aktualizacja nie dotrze do użytkowników przez rok. Plik Service Workera powinien mieć ustawione `Cache-Control: no-cache` lub `max-age=0` – resztę zasobów możesz agresywnie cachować.

Zakres działania Service Workera zależy od lokalizacji pliku `sw.js` na serwerze. Plik umieszczony w `/app/sw.js` kontroluje tylko ścieżki zaczynające się od `/app/`. Jeśli chcesz, żeby SW obejmował całą domenę, umieść go w katalogu głównym. Gdy korzystasz z frameworka, który buduje pliki do podkatalogu, sprawdź konfigurację `scope` w opcjach rejestracji.

Wdrożenie na platformach typu Netlify, Vercel czy Firebase Hosting jest prostsze, bo te serwisy domyślnie wymuszają HTTPS i pozwalają ustawić niestandardowe nagłówki HTTP przez pliki konfiguracyjne. Na Netlify wystarczy plik `_headers` w katalogu głównym, gdzie dla `sw.js` definiujesz `Cache-Control: no-cache`. Na własnym serwerze nginx odpowiednik to blok `location` z dyrektywą `add_header`.

Po wdrożeniu wróć do Lighthouse i uruchom audyt na produkcyjnym URL – nie na `localhost`. Wyniki potrafią się różnić o 20–30 punktów ze względu na realne opóźnienia sieci i inne zasoby ładowane przez zewnętrzne skrypty analityczne czy czcionki. Jeśli raport wskazuje problem z ikonami, sprawdź, czy ścieżki w manifeście są absolutne lub poprawnie relatywne względem lokalizacji pliku `manifest.json`.

Progressive Web Apps – co dalej po pierwszym wdrożeniu?

Działająca aplikacja PWA to punkt startowy, nie meta. Aplikacja zainstalowana na urządzeniu użytkownika powinna się rozwijać, a każda aktualizacja wymaga przemyślanego podejścia do zarządzania Service Workerem.

Gdy publikujesz nową wersję plików, zmień nazwę lub numer wersji w zmiennej `CACHE_NAME` wewnątrz SW. Stary Service Worker pozostaje aktywny do momentu, gdy wszystkie karty z aplikacją zostaną zamknięte – dopiero wtedy nowy SW przejmuje kontrolę i usuwa poprzedni cache. Możesz przyspieszyć ten proces, wywołując `self.skipWaiting` w zdarzeniu `install` i `clients.claim` w zdarzeniu `activate`. Pamiętaj jednak, że agresywne przejęcie kontroli bez informowania użytkownika może spowodować niespójność danych, jeśli stara i nowa wersja aplikacji używają różnych formatów lokalnego storage.

Powiadomienia push to jeden z elementów, który najczęściej pojawia się w kolejnych iteracjach projektu. Backend musi przechowywać subskrypcje użytkowników i wysyłać wiadomości przez protokół Web Push – do tego potrzebny jest serwer Node.js, Python lub dowolny inny z biblioteką obsługującą VAPID. Klucze VAPID generujesz raz i przechowujesz po stronie serwera; klucz publiczny trafia do kodu frontendowego przy wywołaniu `pushManager.subscribe`. Subskrypcja zwraca obiekt zawierający endpoint i klucze szyfrowania – ten obiekt wysyłasz do własnego backendu i zapisujesz w bazie danych.

Warto też monitorować realne zachowanie użytkowników po instalacji aplikacji PWA. Google Analytics 4 i inne narzędzia analityczne pozwalają segmentować ruch według źródła: przeglądarka vs. zainstalowana aplikacja (tryb `standalone`). Możesz to wykryć przez `window.matchMedia('(display-mode: standalone)’).matches` i przekazać tę informację jako wymiar niestandardowy do narzędzia analitycznego. Dane pokazują, czy użytkownicy zainstalowanej wersji wracają częściej i czy ich sesje są dłuższe – to realne argumenty przy ocenie, czy inwestycja w PWA przynosi efekty.

Rozbudowa funkcji offline to kolejny naturalny krok. Podstawowy precaching obejmuje powłokę aplikacji, ale możesz rozszerzyć go o dane użytkownika przechowywane w IndexedDB i synchronizowane w tle przez Background Sync API. Użytkownik wypełnia formularz bez połączenia, a dane trafiają do kolejki – gdy sieć wróci, SW automatycznie wysyła żądanie do serwera. To szczególnie przydatne w aplikacjach terenowych: formularzach inspekcji, raportach serwisowych czy zamówieniach składanych w miejscach ze słabym zasięgiem.

Decyzja o rozbudowie PWA powinna wynikać z danych, nie z możliwości technicznych. Sprawdź w analityce, gdzie użytkownicy rezygnują z korzystania z aplikacji, jakie błędy rejestruje Sentry lub inny system monitorowania błędów i które zasoby najdłużej się ładują. Każda godzina spędzona na optymalizacji krytycznej ścieżki renderowania przynosi więcej niż wdrożenie kolejnej funkcji, z której korzysta 3% użytkowników.

Zbudowanie pierwszej aplikacji PWA wymaga opanowania kilku niezależnych mechanizmów naraz: manifestu, Service Workera, strategii cache i audytu wydajności. Zacznij od najprostszej wersji – statycznej strony z plikiem SW obsługującym tryb offline – a kolejne warstwy dodawaj stopniowo, weryfikując każdą zmianę w Lighthouse i na prawdziwym urządzeniu. Jeśli TTI twojej aplikacji przekracza 4 sekundy, to właśnie tam warto skupić uwagę przed wdrożeniem powiadomień push czy Background Sync.

Podziel się