premik.pl

Jak tworzyć nowoczesne PWA (Progressive Web Apps)

Osoba ubrana w dżinsy i białe trampki siedzi na kamiennych schodach, trzymając i używając smartfona z różnymi kolorowymi ikonami aplikacji widocznymi na ekranie.

Użytkownik instaluje twoją aplikację na telefonie bez odwiedzania App Store. Działa offline. Wysyła powiadomienia push. Ładuje się w ułamku sekundy. Brzmi jak natywna aplikacja mobilna – a to tylko strona internetowa. Witaj w świecie Progressive Web Apps.

Czym są PWA i co wyróżnia je na tle zwykłych stron?

Progressive Web App to aplikacja webowa, która dzięki zestawowi nowoczesnych technologii zachowuje się jak natywna aplikacja na urządzeniu użytkownika. Można ją zainstalować na ekranie głównym telefonu lub pulpicie komputera, uruchomić bez przeglądarki, używać offline i otrzymywać z niej powiadomienia – tak jak z każdej innej aplikacji pobranej ze sklepu.

Koncepcja PWA narodziła się w Google w 2015 roku i od tamtej pory zyskała szerokie wsparcie wszystkich głównych przeglądarek. Dziś PWA napędza globalne produkty takie jak Twitter Lite, Pinterest, Uber czy Spotify Web Player. Pinterest po wdrożeniu PWA odnotował wzrost czasu spędzanego w aplikacji o 40%, a konwersji o 60% – na rynkach z wolniejszym internetem efekty były jeszcze bardziej spektakularne.

Co odróżnia PWA od zwykłej strony responsywnej? Trzy filary: Service Worker odpowiedzialny za działanie offline i cache, Web App Manifest definiujący wygląd i zachowanie po instalacji oraz HTTPS jako obowiązkowa podstawa bezpieczeństwa. Dopiero połączenie tych trzech elementów sprawia, że przeglądarka traktuje aplikację jako PWA i oferuje użytkownikowi możliwość instalacji.

Web App Manifest – powiedz przeglądarce, jak wyglądać po instalacji

Manifest to plik JSON, który opisuje twoją aplikację – jej nazwę, ikony, kolory i zachowanie po uruchomieniu. To od niego zaczyna się każde PWA.

Tworzysz plik manifest.json w katalogu głównym projektu:

json

{
  "name": "Moja Aplikacja",
  "short_name": "Aplikacja",
  "description": "Opis tego, co robi aplikacja",
  "start_url": "/",
  "display": "standalone",
  "background_color": "#ffffff",
  "theme_color": "#2563eb",
  "orientation": "portrait-primary",
  "icons": [
    {
      "src": "/icons/icon-192.png",
      "sizes": "192x192",
      "type": "image/png",
      "purpose": "any maskable"
    },
    {
      "src": "/icons/icon-512.png",
      "sizes": "512x512",
      "type": "image/png",
      "purpose": "any maskable"
    }
  ]
}

Kilka kluczowych pól wymaga wyjaśnienia. display: "standalone" sprawia, że aplikacja po uruchomieniu wygląda jak natywna – bez paska adresu przeglądarki. Alternatywą jest "fullscreen" (cały ekran bez żadnych elementów UI) lub "browser" (standardowy widok przeglądarki). theme_color ustawia kolor paska statusu na Androidzie – warto dopasować go do palety kolorów aplikacji. Ikony w rozmiarach 192×192 i 512×512 px to minimum – warto dodać też inne rozmiary dla lepszego wyglądu na różnych urządzeniach.

Manifest podłączasz do HTML przez tag <link> w sekcji <head>:

html

<link rel="manifest" href="/manifest.json">
<meta name="theme-color" content="#2563eb">

Service Worker – serce każdego PWA

Service Worker to skrypt JavaScript działający w tle, niezależnie od strony. To on odpowiada za przechwytywanie żądań sieciowych, zarządzanie cache i umożliwienie działania offline. Bez Service Workera nie ma prawdziwego PWA.

Rejestrujesz go w głównym pliku JavaScript aplikacji:

if ('serviceWorker' in navigator) {
  window.addEventListener('load', () => {
    navigator.serviceWorker.register('/sw.js')
      .then(reg => console.log('Service Worker zarejestrowany:', reg.scope))
      .catch(err => console.error('Błąd rejestracji:', err))
  })
}

Sam plik sw.js obsługuje trzy kluczowe zdarzenia – install, activate i fetch. Najpopularniejsza strategia cachowania na start to Cache First with Network Fallback: najpierw sprawdź cache, jeśli czegoś nie ma – pobierz z sieci i zapisz do cache:

const CACHE_NAME = 'moja-aplikacja-v1'
const ASSETS_TO_CACHE = ['/', '/index.html', '/styles.css', '/app.js']

self.addEventListener('install', event => {
  event.waitUntil(
    caches.open(CACHE_NAME).then(cache => cache.addAll(ASSETS_TO_CACHE))
  )
})

self.addEventListener('fetch', event => {
  event.respondWith(
    caches.match(event.request).then(cached => {
      return cached || fetch(event.request).then(response => {
        return caches.open(CACHE_NAME).then(cache => {
          cache.put(event.request, response.clone())
          return response
        })
      })
    })
  )
})

self.addEventListener('activate', event => {
  event.waitUntil(
    caches.keys().then(keys =>
      Promise.all(keys.filter(k => k !== CACHE_NAME).map(k => caches.delete(k)))
    )
  )
})

Zdarzenie activate czyści stare wersje cache – ważne przy aktualizacjach aplikacji, żeby użytkownicy nie utknęli ze starymi zasobami.

W praktyce rzadko pisze się Service Workera od zera. Biblioteka Workbox od Google abstrahuje najczęstsze wzorce cachowania do kilku linii kodu i obsługuje edge case’y, które przy ręcznej implementacji łatwo pominąć. Jeśli używasz Vite, plugin vite-plugin-pwa konfiguruje Workbox automatycznie na podstawie prostego pliku konfiguracyjnego.

Powiadomienia push i instalacja – zaawansowane możliwości PWA

Dwie funkcje, które najbardziej zbliżają PWA do natywnych aplikacji, to powiadomienia push i natywny prompt instalacji.

Push Notifications wymagają po stronie frontendu uzyskania zgody użytkownika i zsubskrybowania go przez Push API:

async function subscribeToPush() {
  const permission = await Notification.requestPermission()
  if (permission !== 'granted') return

  const registration = await navigator.serviceWorker.ready
  const subscription = await registration.pushManager.subscribe({
    userVisibleOnly: true,
    applicationServerKey: urlBase64ToUint8Array(PUBLIC_VAPID_KEY)
  })

  // Wyślij subscription do swojego backendu
  await fetch('/api/push/subscribe', {
    method: 'POST',
    body: JSON.stringify(subscription),
    headers: { 'Content-Type': 'application/json' }
  })
}

Po stronie serwera potrzebujesz biblioteki do wysyłania powiadomień – w Node.js to web-push, w PHP minishlink/web-push. Klucze VAPID (para klucza publicznego i prywatnego) generujesz raz i przechowujesz bezpiecznie po stronie serwera.

Prompt instalacji – przeglądarka sama decyduje, kiedy zaproponować instalację, ale możesz przechwycić to zdarzenie i pokazać własny, kontekstowy przycisk w odpowiednim momencie:

javascript

let deferredPrompt

window.addEventListener('beforeinstallprompt', event => {
  event.preventDefault()
  deferredPrompt = event
  showInstallButton() // Pokaż własny przycisk "Zainstaluj aplikację"
})

async function installApp() {
  if (!deferredPrompt) return
  deferredPrompt.prompt()
  const { outcome } = await deferredPrompt.userChoice
  console.log(outcome === 'accepted' ? 'Zainstalowano' : 'Odrzucono')
  deferredPrompt = null
}

Narzędzia, frameworki i audyt PWA

Tworzenie PWA od zera jest możliwe, ale ekosystem frameworków znacznie upraszcza pracę i eliminuje typowe błędy.

Vite + vite-plugin-pwa to dziś najwygodniejsze podejście dla projektów opartych na Vue, React czy Svelte. Plugin automatycznie generuje Service Workera przez Workbox, obsługuje precaching zasobów i aktualizacje – wystarczy kilkanaście linii konfiguracji.

Next.js z pakietem next-pwa oferuje podobną wygodę w ekosystemie React z Server-Side Rendering.

Nuxt ma wbudowany moduł @vite-pwa/nuxt, który integruje PWA z ekosystemem Nuxt 3 bez dodatkowej konfiguracji.

Niezależnie od wybranego frameworka, jakość PWA weryfikujesz przez Lighthouse w Chrome DevTools – zakładka Lighthouse, kategoria Progressive Web App. Narzędzie sprawdza obecność manifestu, Service Workera, HTTPS, responsywności i innych kryteriów instalacyjności, przyznając wynik i wskazując konkretne braki do uzupełnienia.

Warto też zainstalować swoje PWA na prawdziwym urządzeniu – Chrome na Androidzie, Safari na iOS – i przetestować działanie offline przez odcięcie połączenia w ustawieniach telefonu. Symulacja w DevTools to dobry start, ale prawdziwe urządzenie ujawnia problemy, których emulator nie pokaże.

PWA to technologia, która eliminuje największy podział w web developmencie – między aplikacjami webowymi a natywnymi. Inwestycja w jej wdrożenie zwraca się szczególnie na rynkach z wolniejszym internetem i wśród użytkowników mobilnych. Jeśli masz pytania dotyczące konkretnej implementacji lub chcesz omówić, czy PWA to dobra decyzja dla twojego projektu – napisz przez formularz kontaktowy.

Aktualne wsparcie poszczególnych API PWA w różnych przeglądarkach sprawdzisz na whatwebcando.today – szczególnie przydatne przed podjęciem decyzji o wdrożeniu konkretnych funkcji.

Zobacz powiązane wpisy