Tutorial: Erste Schritte mit Progressive Web Apps

10.06.2024 von Matthew Tyson
Progressive Web Apps sind komplexer zu entwickeln als traditionelle Webanwendungen. Lesen Sie, warum sich dieser Zusatzaufwand lohnt.
Progressive Web Apps sind ein innovativer Ansatz der modernen Webentwicklung.
Foto: KatePilko | shutterstock.com

Progressive Web Apps (PWA) verbinden die Allgegenwärtigkeit von Webbrowsern mit der Funktionsvielfalt nativer Applikationen. Sie sind dabei zwar aufgrund spezialisierter Features schwieriger zu entwickeln, bieten im Gegenzug jedoch einen enormen Vorteil: Geräteübergreifende, Native-ähnliche Funktionen - serviert in einem Browser. Auch langfristig betrachtet lohnt es sich, die zusätzliche Entwicklungskomplexität auf sich zu nehmen: PWA erfordern lediglich eine singuläre Codebasis und ermöglichen parallel, mit vertrauten Browser-Standards zu arbeiten.

In diesem Tutorial lesen Sie, welche Features Progressive Web Apps auszeichnen, wie Sie sie installieren - und bereitstellen.

Das können Progressive Web Apps

Ein charakteristisches Merkmal von nativen Apps ist, dass sie auch laufen, wenn keine Netzwerkverbindung existiert. Progressive Web Apps unterstützen ähnliche Offline-Funktionalitäten innerhalb eines Browsers. Im Gegensatz zu Browser-basierten Webanwendungen sind PWA allerdings stark vom Anwendungstyp abhängig: Die Funktionen der App bestimmen maßgeblich darüber, wie Progressive Web Apps implementiert werden.

Zu den allgemeinen Features von Progressive Web Apps zählen:

Ein guter Anwendungsfall für eine Progressive Web App ist Google Docs. Die Browser-basierte Anwendung unterstützt einen Offline-Modus, wenn kein Netzwerk verfügbar ist. Im Wesentlichen kann die App dabei sämtliche Inhalte lokal im Browser speichern und mit dem Backend synchronisieren, sobald der Browser wieder online ist.

Was uns zur Komplexität von PWA führt, die sich im Wesentlichen darin begründet, dass distribuierte Teile einer Anwendung synchronisiert werden müssen. Google Docs stellt ein entsprechend massives, architektonisches Unterfangen dar. Es geht aber auch ein bisschen simpler: Wie schon erwähnt, gilt der Grundsatz, dass die Anforderungen der Anwendung darüber bestimmen, wie umfangreich Progressive Web Apps implementiert werden müssen.

Progressive Web Apps installieren

Eine Besonderheit von Progressive Web Apps: Sie können "installiert" werden, obwohl sie im Browser laufen. Auf dem Frontend wird dabei ein Link auf der Startseite des Devices platziert, der die Website im Browser aufruft. Die PWA-Installation selbst erfolgt über eine manifest.json-Datei, die dem Browser die App-Funktionen und ihr Homepage-Icon beschreibt. Im Folgenden ein Beispiel für ein einfaches Manifest:

{

"name": "My PWA App",

"short_name": "My PWA",

"icons": [

{

"src": "icons/icon-192x192.png",

"sizes": "192x192",

"type": "image/png"

}

],

"start_url": "/",

"display": "standalone",

"theme_color": "#ffffff"

}

Findet der Browser eine solche Datei im Root-Verzeichnis, bietet er an, einen Link zur Homepage hinzuzufügen (vorausgesetzt, die Datei ist validiert).

PWA-Funktionen über Service Worker bereitstellen

Bereitgestellt werden PWA-Funktionen hauptsächlich über sogenannte Service Worker. Zugriff auf die Service Worker API ermöglicht Ihnen das navigator.serviceWorker-Objekt, das ausschließlich in einem sicheren (HTTPS) Kontext verfügbar ist. Ein Service Worker ist so etwas Ähnliches wie ein Worker Thread, weist aber einen längerfristigen Lebenszyklus auf und greift weniger auf DOM und Browser-APIs zu.

Stellen Sie sich einen Service Worker als einen isolierten Kontext vor, der mit Ihrem Haupt-Thread (und anderen Workern) über Messages und Events interagieren kann: Er reagiert auf Events, fährt Netzwerk-Requests, antwortet auf Push-Calls und speichert Informationen über die Cache API oder über IndexedDB. Ein Service Worker kann dabei nur über Nachrichten an den Haupt-Thread auf das User Interface einwirken. Man könnte die Auffassung vertreten, dass ein Service Worker eine Proxy-Middleware darstellt, die im Browser läuft.

In Sachen Lebenszyklus weisen Service Worker Besonderheiten auf. Spezifische Bedingungen bestimmen darüber, wann sie beendet werden. Sie können Benutzern auf Push-Basis auch dann noch Benachrichtigungen übermitteln, wenn die Seite, die sie hervorgebracht hat, bereits geschlossen wurde. Zudem gibt es auch Browser-spezifische Informationen darüber, wie Service Worker zu beenden sind - zum Beispiel für Chrome.

Service Worker Events

Im Wesentlichen sind Service Worker asynchrone Event Handler: Sie reagieren auf UI- oder Backend-Events. Wenn Sie Service Worker erstellen, sollten Sie dabei im Hinterkopf behalten, dass der Kontext zwischen den Events gelöscht wird. Es ist nicht möglich, den State in lokalen Variablen zu speichern. Stattdessen greifen Sie dazu auf den Cache oder eine Datenbank zurück.

Ein Service Worker kann auf folgende Events reagieren:

Darüber hinaus können Service Worker auch auf verschiedene APIs zugreifen:

Ein Service-Worker-Beispiel

Ein Service Worker wird initial stets mit dem navigator.serviceWorker-Objekt in ein JavaScript-File geladen - etwa so:

const subscription = await navigator.serviceWorker.register('service-worker.js');

Event Subscrptions erfolgen in service-worker.js. Um beispielsweise ein fetch-Event mit der Cache API in Augenschein zu nehmen, könnten Sie wie folgt vorgehen:

self.addEventListener('fetch', (event) => {

const request = event.request;

const url = new URL(request.url);

// Try serving assets from cache first

event.respondWith(

caches.match(request)

.then((cachedResponse) => {

// If found in cache, return the cached response

if (cachedResponse) {

return cachedResponse;

}

// If not in cache, fetch from network

return fetch(request)

.then((response) => {

// Clone the response for potential caching

const responseClone = response.clone();

// Cache the new response for future requests

caches.open('my-cache')

.then((cache) => {

cache.put(request, responseClone);

});

return response;

});

})

);

});

Wenn der Service Worker geladen ist, verweist self auf ihn. Mit der addEventListener-Methode können Sie nach verschiedenen Ereignissen Ausschau halten. Innerhalb des fetch-Events können Sie mit Hilfe der Cache API prüfen, ob die angegebene Anfrage-URL bereits im Cache vorhanden ist. Falls ja, wird sie zurückgesendet. Handelt es sich um eine neue URL, stellen Sie Ihren Request an den Server und speichern die Antwort zwischen. Die Cache-API kann die Komplexität deutlich reduzieren, wenn es darum geht, herauszufinden, ob es sich um denselben Request handelt oder nicht. Service Worker machen das für den Haupt-Thread transparent. (fm)