Artikel

Progressive Web Apps (PWAs)

Progressive Web Apps (PWAs)

Eine Progressive Web App (PWA) ist eine Webanwendung, die moderne Browserfunktionen nutzt, um ein App-ähnliches Erlebnis zu bieten. PWAs sind zuverlässig, schnell und ansprechend – sie kombinieren die Reichweite des Webs mit den Funktionen nativer mobiler Anwendungen.

5 Min. LesezeitSprache: DE DeutschKostenlos0 Claps0 Kommentare
TechnologieEngineering-ArtikelAppsProgressiveWeb DevelopmentTechnologyEngineering ArticlesPWAs
Leseoptionen

Einführung

Eine Progressive Web App (PWA) ist eine Webanwendung, die moderne Browserfunktionen nutzt, um ein App-ähnliches Erlebnis zu bieten. PWAs sind zuverlässig, schnell und ansprechend – sie kombinieren die Reichweite des Webs mit den Funktionen nativer mobiler Anwendungen.

PWAs lösen ein grundlegendes Problem: Benutzer möchten die sofortige Verfügbarkeit des Webs (keine Installation, verlinkbar, immer aktuell), erwarten aber die Leistung und Funktionen nativer Apps (Offline-Unterstützung, Push-Benachrichtigungen, Präsenz auf dem Startbildschirm). PWAs bieten beides.


Kernmerkmale

Der Begriff „Progressive Web App“ wurde 2015 von Alex Russell und Frances Berriman von Google geprägt. Eine PWA muss diese 10 Merkmale erfüllen:

# Prinzip Warum es wichtig ist
1 Progressiv – Funktioniert für jeden Anmutige Verschlechterung bei älteren Browsern
2 Responsiv – Passt auf jeden Bildschirm Desktop, Tablet, Mobil
3 Konnektivitätsunabhängig – Funktioniert offline Caching von Servicemitarbeitern
4 App-ähnlich – Natives App-Feeling Shell-Architektur, reibungslose Navigation
5 Frisch – Immer aktuell Service-Worker-Update-Lebenszyklus
6 Sicher – Wird über HTTPS bereitgestellt Verhindert Manipulationen, erforderlich für Servicemitarbeiter
7 Auffindbar – SEO-freundlich Suchmaschinen indizieren PWAs
8 Wieder aktivierbar – Push-Benachrichtigungen Binden Sie Benutzer wie native Apps erneut ein
9 Installierbar – Zum Startbildschirm hinzufügen Web-App-Manifest
10 Verlinkbar – Über URL teilen Kein App-Store erforderlich

Die drei Säulen von PWA

1. Servicemitarbeiter – Der Motor

Ein Service Worker ist eine JavaScript-Datei, die getrennt von der Webseite im Hintergrund ausgeführt wird. Es fungiert als programmierbarer Netzwerk-Proxy, der Anfragen abfängt und Offline-Funktionalität, Push-Benachrichtigungen und Hintergrundsynchronisierung ermöglicht.

Lebenszyklus von Servicemitarbeitern

Installing → Installed → Activating → Activated → Idle → Terminated
                 │                                        │
                 └─── (new version detected) ──────────────┘
                 └─── Waiting (until all tabs close) ─────┘
// service-worker.js
const CACHE_NAME = 'my-pwa-v2';
const ASSETS_TO_CACHE = [
  '/',
  '/index.html',
  '/styles.css',
  '/app.js',
  '/offline.html',
  '/icons/icon-192.png'
];

// Install event — cache static assets
self.addEventListener('install', event => {
  event.waitUntil(
    caches.open(CACHE_NAME).then(cache => {
      console.log('Caching app shell');
      return cache.addAll(ASSETS_TO_CACHE);
    })
  );
  // Force new service worker to activate immediately
  self.skipWaiting();
});

// Activate event — clean old caches
self.addEventListener('activate', event => {
  event.waitUntil(
    caches.keys().then(cacheNames => {
      return Promise.all(
        cacheNames
          .filter(name => name !== CACHE_NAME)
          .map(name => caches.delete(name))
      );
    })
  );
  // Take control of all open pages immediately
  clients.claim();
});

// Fetch event — intercept network requests
self.addEventListener('fetch', event => {
  event.respondWith(
    caches.match(event.request).then(cachedResponse => {
      // Return cached response if available
      if (cachedResponse) {
        return cachedResponse;
      }

      // Otherwise fetch from network
      return fetch(event.request).then(networkResponse => {
        // Don't cache non-GET or error responses
        if (!event.request.url.includes('/api/')) {
          return networkResponse;
        }

        // Cache API responses for offline use
        const responseClone = networkResponse.clone();
        caches.open(CACHE_NAME).then(cache => {
          cache.put(event.request, responseClone);
        });

        return networkResponse;
      }).catch(() => {
        // Network failed — return offline fallback
        if (event.request.mode === 'navigate') {
          return caches.match('/offline.html');
        }
        return new Response('Offline', { status: 503 });
      });
    })
  );
});

Caching-Strategien

Strategie Beschreibung Anwendungsfall
Zuerst zwischenspeichern Überprüfen Sie den Cache und greifen Sie auf das Netzwerk zurück Statische Assets (CSS, JS, Bilder)
Netzwerk zuerst Versuchen Sie es mit dem Netzwerk und greifen Sie auf den Cache zurück API-Aufrufe, dynamische Inhalte
Veraltet während der erneuten Validierung Cache zurückgeben, im Hintergrund aktualisieren Newsfeeds, Blogbeiträge
Nur Netzwerk Immer aus dem Netzwerk abrufen Sensible Daten (Banking)
Nur Cache Niemals aus dem Netzwerk abrufen App-Shell
// Stale-while-revalidate strategy
self.addEventListener('fetch', event => {
  event.respondWith(
    caches.match(event.request).then(cachedResponse => {
      const fetchPromise = fetch(event.request).then(networkResponse => {
        caches.open(CACHE_NAME).then(cache => {
          cache.put(event.request, networkResponse.clone());
        });
        return networkResponse;
      }).catch(() => cachedResponse);

      return cachedResponse || fetchPromise;
    })
  );
});

2. Web-App-Manifest – Die Identität

Das Manifest ist eine JSON-Datei, die steuert, wie die PWA bei der Installation angezeigt wird:

{
  "name": "My Progressive Web App",
  "short_name": "MyPWA",
  "description": "An amazing PWA that works offline",
  "start_url": "/?source=pwa",
  "display": "standalone",
  "background_color": "#ffffff",
  "theme_color": "#3367D6",
  "orientation": "portrait-primary",
  "scope": "/",
  "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"
    }
  ],
  "categories": ["productivity", "utilities"],
  "lang": "en-US",
  "dir": "ltr",
  "prefer_related_applications": false
}

Manifeste Eigenschaften erklärt:

  • „display: standalone“ – Öffnet ohne Browser-Benutzeroberfläche (Adressleiste, Registerkarten).
  • „display: fullscreen“ – Vollbild, überhaupt kein Browser-Chrome.
  • „display: minimal-ui“ – Minimale Browser-Steuerelemente.
  • scope: „/“ – Welche URLs sind Teil der PWA.
  • „start_url“ – Die Seite, die geöffnet wird, wenn die PWA gestartet wird.
  • Maskierbare Symbole – Adaptive Symbole, die mit Maskierung auf Betriebssystemebene funktionieren (abgerundet, kreisförmig usw.).

3. HTTPS – Die Stiftung

Servicemitarbeiter benötigen einen sicheren Kontext. HTTPS ist obligatorisch für:

  • Registrierung von Servicemitarbeitern.
  • Push-Benachrichtigungen.
  • Die Installationsaufforderung (vor dem Unload-Ereignis).
# Redirect HTTP to HTTPS
# Nginx
server {
    listen 80;
    server_name example.com;
    return 301 https://$server_name$request_uri;
}

Aufbau einer PWA – Schritt für Schritt

1. Erstellen Sie die App Shell

Die App-Shell besteht aus dem minimalen HTML-, CSS- und JavaScript-Code, der zum Rendern der Benutzeroberfläche erforderlich ist. Es wird beim ersten Laden zwischengespeichert und dient als Grundlage der PWA.

<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta name="theme-color" content="#3367D6">
  <link rel="manifest" href="/manifest.json">
  <link rel="icon" href="/icons/icon-192.png" sizes="192x192">
  <link rel="apple-touch-icon" href="/icons/icon-192.png">
  <title>My PWA</title>
  <link rel="stylesheet" href="/styles.css">
</head>
<body>
  <header id="app-header">My PWA</header>
  <main id="app-content"></main>
  <footer id="app-footer">© 2026</footer>

  <!-- Register service worker -->
  <script>
    if ('serviceWorker' in navigator) {
      window.addEventListener('load', () => {
        navigator.serviceWorker.register('/service-worker.js')
          .then(reg => console.log('SW registered:', reg.scope))
          .catch(err => console.error('SW registration failed:', err));
      });
    }
  </script>
  <script src="/app.js"></script>
</body>
</html>

2. Implementieren Sie den Offline-Support

// app.js — Fetch content with offline support
const CACHE_KEY = 'content-cache';

async function loadContent(url) {
  try {
    const response = await fetch(url);
    const data = await response.json();

    // Cache for offline
    const cache = await caches.open(CACHE_KEY);
    cache.put(url, new Response(JSON.stringify(data)));

    renderContent(data);
  } catch (error) {
    // Offline — load from cache
    const cached = await caches.match(url);
    if (cached) {
      const data = await cached.json();
      renderContent(data);
      showOfflineIndicator();
    } else {
      showOfflineMessage();
    }
  }
}

3. Fügen Sie Push-Benachrichtigungen hinzu

// Request permission
async function requestNotificationPermission() {
  const permission = await Notification.requestPermission();
  if (permission === 'granted') {
    // Get push subscription
    const registration = await navigator.serviceWorker.ready;
    const subscription = await registration.pushManager.subscribe({
      userVisibleOnly: true,
      applicationServerKey: urlBase64ToUint8Array(publicKey)
    });

    // Send subscription to server
    await fetch('/api/push/subscribe', {
      method: 'POST',
      body: JSON.stringify(subscription)
    });
  }
}
// service-worker.js — Handle push events
self.addEventListener('push', event => {
  const data = event.data.json();

  const options = {
    body: data.body,
    icon: '/icons/icon-192.png',
    badge: '/icons/badge-72.png',
    vibrate: [100, 50, 100],
    data: { url: data.url },
    actions: [
      { action: 'open', title: 'Open' },
      { action: 'close', title: 'Dismiss' }
    ]
  };

  event.waitUntil(
    self.registration.showNotification(data.title, options)
  );
});

self.addEventListener('notificationclick', event => {
  event.notification.close();

  if (event.action === 'open' || !event.action) {
    clients.openWindow(event.notification.data.url);
  }
});

PWA vs. native App

Funktion PWA Native App
Verteilung URL (kein Shop erforderlich) App Store, Google Play
Reibung einbauen Ein Fingertipp (Browser-Eingabeaufforderung) Herunterladen, installieren, Berechtigungen
Dateigröße KB zu MB 10 MB bis 500 MB+
Aktualisierungen Instant (Servicemitarbeiter) Via App-Store-Rezension
Offline Ja (Servicemitarbeiter) Ja (eingebaut)
Push-Benachrichtigungen Ja Ja
Geräte-APIs Kamera, GPS, Beschleunigungsmesser, Zahlung Voller Zugriff auf alle Geräte-APIs
Bluetooth Web-Bluetooth (begrenzt) Vollständige Bluetooth-API
Hintergrundaufgaben Hintergrundsynchronisierung (begrenzt) Vollständige Hintergrundausführung
Leistung Nahezu nativ (JavaScript/WebAssembly) Native (kompilierter Code)
SEO Indexierbar durch Suchmaschinen Nicht indexierbar
Entwicklungskosten Einzelne Codebasis iOS + Android (oder plattformübergreifend)
Benutzerbindung Niedriger (leichter zu verlassen) Höher (installiert = festgeschrieben)

Wann sollte man sich für PWA anstelle von Native entscheiden?

  • Inhaltsorientierte Apps – Nachrichten, Blogs, Zeitschriften.
  • E-Commerce – Shopping-Websites (Pinterest PWA: 40 % mehr Zeitaufwand).
  • Dienstprogramm-Apps – Rechner, Umrechner, Wetter.
  • Apps mit geringer Interaktion – Apps, die Benutzer selten besuchen.
  • Schwellenmärkte – Benutzer mit begrenztem Speicherplatz und begrenzten Daten.
  • Progressive Verbesserung – Volle Erfahrung auf modernen Geräten, funktionsfähig auf älteren Geräten.

Wenn eine native App besser ist

  • Hochleistungs-Gaming – 60+ FPS, komplexes 3D-Rendering.
  • Umfassende Hardware-Integration – Bluetooth-Peripheriegeräte, NFC, ARKit/ARCore.
  • Hintergrundverarbeitung – Musik-Streaming, Fitness-Tracking.
  • Hohes Benutzerengagement erforderlich – Soziale Medien, Messaging.

Leistungsbenchmarks

Metrisch Vor PWA Nach PWA Verbesserung
Erster Ladevorgang 5s 1,2s 76 % schneller
Nachfolgende Ladungen 3s 0,3s 90 % schneller
Absprungrate 45% 20% 55 % Ermäßigung
Conversion-Rate 2% 3.5% Steigerung um 75 %
Seiten pro Sitzung 3 5 Steigerung um 66 %

Ergebnisse aus der Praxis:

Unternehmen Schlüsselmetrik Verbesserung
Twitter (X) Lite Pro Sitzung gesendete Tweets Steigerung um 75 %
Pinterest Zeitaufwand Steigerung um 40 %
Uber Buchungsabschluss Reibungslos, auch bei 2G
Starbucks Täglich aktive Benutzer 2x Erhöhung
Alibaba Conversion-Rate Steigerung um 76 %

PWA-Tests und Audits

Leuchtturm-PWA-Audit

# Run Lighthouse from CLI
npx lighthouse https://example.com --view --preset=desktop

# PWA-specific checks:
# - ✅ Registers a service worker
# - ✅ Responds with 200 when offline
# - ✅ Web app manifest exists
# - ✅ Manifest has display: standalone
# - ✅ Served over HTTPS
# - ✅ Redirects HTTP to HTTPS
# - ✅ Configurable start URL
# - ✅ Icons provided (192px and 512px)

Checkliste für manuelle Tests

## PWA Pre-Flight Checklist
- [ ] HTTPS enabled
- [ ] Service worker registered and active
- [ ] Pages load offline (test with airplane mode)
- [ ] Manifest configured with correct icons
- [ ] "Add to Home Screen" prompt appears
- [ ] PWA opens in standalone mode with no address bar
- [ ] Splash screen displays correctly
- [ ] Push notifications work
- [ ] All pages are responsive (mobile, tablet, desktop)
- [ ] Performance budget met (Lighthouse score > 90)
- [ ] Cross-browser tested (Chrome, Firefox, Safari, Edge)
- [ ] Accessibility audit passes

Erweiterte PWA-Funktionen

Hintergrundsynchronisierung

// Register a sync event
async function scheduleSync() {
  const registration = await navigator.serviceWorker.ready;
  await registration.sync.register('sync-pending-data');
}

// Service worker handles sync
self.addEventListener('sync', event => {
  if (event.tag === 'sync-pending-data') {
    event.waitUntil(syncPendingData());
  }
});

Regelmäßige Hintergrundsynchronisierung

// Request periodic sync (requires user engagement)
const registration = await navigator.serviceWorker.ready;
await registration.periodicSync.register('update-content', {
  minInterval: 24 * 60 * 60 * 1000 // Once per day
});

Web Share-API

// Share content using native share sheet
async function shareContent(title, text, url) {
  if (navigator.share) {
    await navigator.share({ title, text, url });
  } else {
    // Fallback to custom share UI
    copyToClipboard(url);
  }
}

Badge-API

// Set app icon badge
if (navigator.setAppBadge) {
  await navigator.setAppBadge(5); // Show "5" on icon
} else if (navigator.clearAppBadge) {
  await navigator.clearAppBadge();
}

Häufige Fallstricke

Falle Problem Lösung
Übercaching Die App stellt veraltete Inhalte bereit Versions-Caches, Cache-First für die Shell und Network-First für den Inhalt implementieren
Kein Offline-Fallback Weißer Bildschirm offline Cachen Sie /offline.html immer mit einer aussagekräftigen Nachricht
Cache-Busting-Fehler Alter Servicemitarbeiter bleibt bestehen Verwenden Sie SkipWaiting() und Clients.claim()
Probleme mit dem Begrüßungsbildschirm Falsche Designfarbe oder falsches Symbol Konfigurieren Sie das Manifest richtig und verwenden Sie maskierbare Symbole
Safari-Kompatibilität Einige Funktionen eingeschränkt (Push, regelmäßige Synchronisierung) Progressive Verbesserung – sanfter Abbau
Große Cache-Größe Speicherkontingent überschritten (50 MB pro Ursprung) Alte Caches bereinigen, Cache-Größe begrenzen
Start-URL fehlt Der Startbildschirm öffnet eine falsche Seite Geben Sie im Manifest immer start_url an

Fazit

Progressive Web Apps repräsentieren das Beste aus beiden Welten – die Reichweite und Zugänglichkeit des Webs kombiniert mit den Funktionen und dem Benutzererlebnis nativer Anwendungen.

Wann man eine PWA erstellt

  1. Sie wollen maximale Reichweite – Eine App für alle Plattformen.
  2. Benutzer benötigt Offline-Zugriff – Service-Worker-Caching.
  3. Sie möchten sofortiges Laden – App-Shell-Architektur.
  4. Sie möchten eine erneute Interaktion – Push-Benachrichtigungen ohne App Store.
  5. Sie haben eine inhaltsgesteuerte App – Nachrichten, E-Commerce, Dienstprogramme.

Erste Schritte

  1. Fügen Sie eine manifest.json und Symbole hinzu.
  2. Erstellen Sie einen Servicemitarbeiter mit Offline-Fallback.
  3. Führen Sie ein Lighthouse-Audit durch und beheben Sie kritische Probleme.
  4. Testen Sie auf realen Geräten (Android, iOS, Desktop).
  5. Implementieren und messen Sie das Benutzerengagement.

PWAs sind nicht in jedem Szenario ein Ersatz für native Apps – aber für die meisten inhaltsgesteuerten und nützlichen Anwendungen bieten sie ein hervorragendes Benutzererlebnis zu einem Bruchteil der Entwicklungskosten.

Empfohlene Artikel

6 Coding-Patterns, die ich von Senior Engineers gelernt habe
EditorialDE
8 Min.Kostenlos

6 Coding-Patterns, die ich von Senior Engineers gelernt habe

TechnologieCodeCoding
0 Claps
Lesen
Einen privaten KI-Assistenten für Notizen bauen
EmpfohlenDE
9 Min.Kostenlos

Einen privaten KI-Assistenten für Notizen bauen

Mache aus Markdown-Notizen einen durchsuchbaren Assistenten, ohne private Entwürfe offenzulegen.

TechnologiePlattform-LeitfädenKI-SchreibenAIAutomationData
0 Claps
Lesen
Rechnungsdaten mit OCR automatisiert auslesen
EditorialDE
8 Min.Kostenlos

Rechnungsdaten mit OCR automatisiert auslesen

Baue eine Pipeline, die Rechnungen liest, Felder extrahiert und unsichere Werte markiert.

TechnologieEngineering-ArtikelKI-SchreibenAIAutomationData
0 Claps
Lesen

Kommentare

0 Kommentare

Noch keine freigegebenen Kommentare sichtbar. Neue Antworten können moderiert werden.