Was ist eine PWA?
Eine Progressive Web App ist eine Website, die sich wie eine installierte App verhält: Sie funktioniert offline, lädt bei wiederholten Besuchen sofort und kann zum Home-Bildschirm oder Desktop hinzugefügt werden. Es gibt keine einzelne Technologie namens PWA – es ist vielmehr eine Reihe von Funktionen, die man schrittweise implementiert.
Drei Komponenten machen dies möglich:
- HTTPS, damit der Browser Ihrem Origin genügend vertraut, um einen service worker zuzulassen.
- Ein web manifest, das den Namen der App, Icons und den Anzeigemodus beschreibt.
- Ein service worker, ein Hintergrund-Skript, das Netzwerkanfragen abfängt und Caches verwaltet.
Das Ergebnis ist ein Erlebnis, das sich nativ anfühlt, während es gleichzeitig eine Website bleibt, die Sie sofort aktualisieren können.
Das Web Manifest
Das Manifest ist eine JSON-Datei, die im HTML-Head verlinkt wird. Sie teilt der Plattform mit, wie die App nach der Installation dargestellt werden soll.
{
"name": "hackweb Reader",
"short_name": "Reader",
"start_url": "/",
"display": "standalone",
"background_color": "#0b0d0c",
"theme_color": "#0b0d0c",
"icons": [
{ "src": "/icons/192.png", "sizes": "192x192", "type": "image/png" },
{ "src": "/icons/512.png", "sizes": "512x512", "type": "image/png" }
]
}
<!-- index.html -->
<link rel="manifest" href="/manifest.webmanifest" />
<meta name="theme-color" content="#0b0d0c" />
display: "standalone" blendet den Browser-Chrome nach der Installation aus, während die Icons und Farben den Splash-Screen und den Task-Switcher steuern. Ein gültiges Manifest zusammen mit einem Service Worker ist die Voraussetzung dafür, dass der Browser die Installation der App anbietet.
Der Lebenszyklus des Service Workers
Ein Service Worker ist ein separates Skript ohne Zugriff auf das DOM. Er besitzt einen eigenen Lebenszyklus: install, activate und fetch.
// register.js
if ("serviceWorker" in navigator) {
window.addEventListener("load", () => {
navigator.serviceWorker.register("/sw.js");
});
}
Während der install-Phase wird die App-Shell vorab im Cache gespeichert. Während der activate-Phase werden alte Caches bereinigt. Während der fetch-Phase werden Anfragen abgefangen, um zu entscheiden, wie darauf geantwortet wird. Ein neuer Worker wartet, bis alle Seiten, die den alten Worker verwenden, geschlossen wurden, bevor er aktiviert wird – weshalb Updates manchmal einen zweiten Reload erfordern.
Caching-Strategien
Das Herzstück eines Service Workers ist seine Caching-Strategie. Verschiedene Ressourcen erfordern unterschiedliche Ansätze.
- Cache-first — Aus dem Cache ausliefern, bei Bedarf auf das Netzwerk zurückgreifen. Ideal für versionierte, unveränderliche Assets wie gehashte Bundles.
- Network-first — Zuerst das Netzwerk versuchen, bei Fehlern auf den Cache zurückgreifen. Gut für HTML und Daten, die aktuell sein müssen.
- Stale-while-revalidate — Die gecachte Version sofort ausliefern und diese im Hintergrund aktualisieren. Hervorragend für Daten, bei denen eine leichte Veralterung akzeptabel ist.
- Cache-only und network-only — für spezifische Sonderfälle.
// strategies.js
self.addEventListener("fetch", (event) => {
const { request } = event;
if (request.destination === "image") {
event.respondWith(
caches.match(request).then((cached) => cached || fetch(request)),
);
}
});
Die request.destination-Eigenschaft gibt an, welche Art von Ressource gerade abgerufen wird, sodass Sie pro Asset-Typ eine eigene Strategie anwenden können.
Offline-Fallback
Selbst mit Caching werden einige Anfragen fehlschlagen. Bieten Sie einen eleganten Fallback an.
// fallback.js
self.addEventListener("fetch", (event) => {
if (event.request.mode === "navigate") {
event.respondWith(
fetch(event.request).catch(() => caches.match("/offline.html")),
);
}
});
Eine Offline-Seite, eine gecachte Shell oder ein klarer „Sie sind offline“-Status sind weitaus besser als die Standard-Fehlermeldung des Browsers. In Kombination mit Background Sync können Anfragen, die offline fehlgeschlagen sind, automatisch wiederholt werden, sobald die Verbindung wiederhergestellt ist.
Updates und Versionierung
Caching ist leistungsstark, aber leicht falsch zu implementieren. Die Regel ist simpel: Versionieren Sie Ihre Caches und löschen Sie alte Versionen bei der Aktivierung. Andernfalls können Nutzer dauerhaft auf veralteten Dateien feststecken.
// activate.js
const CACHE = "app-v2";
self.addEventListener("activate", (event) => {
event.waitUntil(
caches.keys().then((keys) =>
Promise.all(keys.filter((k) => k !== CACHE).map((k) => caches.delete(k))),
),
);
});
Für alles, was über die Grundlagen hinausgeht, übernimmt die Workbox-Library das Precaching, Routing, die Ablaufsteuerung und die Bereinigung mit bewährten Standardeinstellungen und lässt sich in die meisten Build-Tools integrieren.
Mehr als nur Offline-Funktionalität
Service Worker ermöglichen weit mehr als nur das Caching:
- Background sync versucht fehlgeschlagene Anfragen erneut, sobald die Netzwerkverbindung wiederhergestellt ist.
- Push notifications liefern Nachrichten aus, selbst wenn die App geschlossen ist.
- Periodic sync aktualisiert Inhalte im Hintergrund.
- Share target ermöglicht es Ihrer App, geteilte Inhalte vom Betriebssystem zu empfangen.
Jede dieser Funktionen erfordert die Zustimmung des Nutzers und sollte sparsam eingesetzt werden, aber genau sie sorgen dafür, dass sich eine PWA wie eine vollwertige App anfühlt.
Best Practices
- Über HTTPS bereitstellen und den Worker nach dem Laden registrieren.
- Caches versionieren und alte Versionen beim
activate-Event löschen. - Für gehashte Assets die „Cache-First“-Strategie und für HTML die „Network-First“-Strategie verwenden.
- Immer einen Offline-Fallback bereitstellen.
- Nur die App Shell precachen, nicht die gesamte Website.
- Workbox in Betracht ziehen, anstatt komplexe Strategien manuell zu schreiben.
- Den Offline-Modus und Updates vor dem Deployment testen.
Häufige Fehler
- HTML dauerhaft zu cachen und dadurch veraltete Seiten auszuliefern.
- Das Bereinigen alter Caches nach einem Deployment zu vergessen.
- Opaque Cross-Origin-Responses zu cachen, ohne deren Einschränkungen zu kennen.
- Alles vorab zu cachen (Precaching), was die erste Installation unnötig aufbläht.
- Davon auszugehen, dass der neue Worker sofort aktiviert wird.
- Die Storage-Quota zu ignorieren und Caches unbegrenzt anwachsen zu lassen.
Wie geht es weiter?
Ein Service Worker ist der größte Einzelsprung bei der Performance für wiederkehrende Besuche und das Fundament für die Offline-Unterstützung. Kombinieren Sie ihn mit den Techniken aus dem Guide zu Web Performance, verstehen Sie die HTTP-Requests, die er abfängt, und stellen Sie die App über ein Node.js-Backend bereit. Fügen Sie anschließend einem bestehenden Projekt ein Manifest und einen einfachen Cache-First-Worker hinzu und erleben Sie, wie die Seite offline funktioniert.