Progressive Web Apps (PWA) sind ein webbasiertes, plattformübergreifendes Anwendungsmodell: Dieser Typ Anwendung wird exakt einmal auf Basis moderner Webtechnologien entwickelt und kann danach nicht nur in den gängigen Webbrowsern ausgeführt, sondern auch auf diversen Mobil- und Desktopbetriebssystemen installiert werden. Vom Homebildschirm oder aus der Programmliste heraus gestartet, werden diese Anwendungen vollflächig, beziehungsweise in einem eigenen Fenster angezeigt und unterscheiden sich somit nicht von ihren nativen Gegenstücken. Insgesamt bietet sich das Anwendungsmodell für all diejenigen an, die sich keine mehrfache Entwicklung derselben App für unterschiedliche Plattformen leisten können oder wollen.
Hier auf dem Host-Europe-Blog haben wir Ihnen vor einigen Monaten die Entwicklung von Progressive Web Apps mit React vorgestellt. Aus Sicht der Progressive Web Apps ist es unerheblich, mit welchem Framework die jeweilige App entwickelt wird. Somit kommt auch Angular infrage, das beliebte Single-Page-App-Framework von Google. Gemeinsam mit Microsoft treibt Google das PWA-Modell stark voran. Daher verwundert es kaum, dass auch für Angular eine Unterstützung für Progressive Web Apps verfügbar ist. Der PWA-Support ist zwar nicht standardmäßig aktiviert, lässt sich aber im Handumdrehen einschalten – auch für bestehende Projekte. Dazu wird auf der Kommandozeile der folgende Angular-CLI-Befehl ausgeführt:
ng add @angular/pwa
Typischerweise definiert man Progressive Web Apps anhand von zehn Eigenschaften, an denen sich der folgende Blogpost orientiert. Die oben genannte PWA-Unterstützung für Angular setzt manche dieser Eigenschaften schon um, bei anderen müssen Entwickler aktiv werden. Manche Punkte sind harte Anforderungen, andere eher weiche Faktoren. Schauen wir rein!
Progressive
Das Wort „Progressive“ steckt bereits im Namen des Anwendungsmodells: Diese Eigenschaft besagt, dass Progressive Web Apps, ausgeführt in älteren Webbrowsern wie zum Beispiel dem Internet Explorer 11, nicht brechen sollen. Dieser Browser versteht weder die PWA-Schnittstellen noch andere moderne Web-APIs. Wenn die Anwendung grundsätzlich aber auch in älteren Browsern laufen kann, etwa wenn sie primär eine Forms-over-Data-Anwendung ist, sollte die Verwendung moderner Schnittstellen aber nicht dazu führen, dass die Anwendung in älteren Browsern bricht.
Um die Unterstützung älterer Browser sicherzustellen, gibt es das Prinzip des Progressive Enhancement. Hierbei wird zunächst geprüft, ob der Webbrowser eine bestimmte Schnittstelle unterstützt, bevor sie genutzt wird. In Webbrowsern ohne Unterstützung käme es andernfalls zu einem Fehler. Die Angular-PWA-Unterstützung kümmert sich bereits um das Progressive Enhancement in Bezug auf die PWA-Schnittstellen. Werden innerhalb der Anwendung andere Webschnittstellen verwendet, muss sich der Entwickler selbst darum kümmern.
Glücklicherweise geht es beim Progressive Enhancement nur um eine Verzweigung: Wird die API unterstützt, wird sie verwendet. Andernfalls kann die Funktion in der Benutzeroberfläche versteckt werden, oder es wird eine Fallback-Methode genutzt. So könnte es etwa bei der Web Share API funktionieren, die unter Chrome auf Android sowie Safari unter iOS und macOS den nativen Teilen-Dialog des Betriebssystems anzeigt. Auf anderen Systemen gibt es die API derzeit nicht. Steht die share()-Methode auf dem navigator-Objekt zur Verfügung, wird sie aufgerufen, andernfalls wird auf das weitaus besser unterstützte Mailto-Protokoll zurückgegriffen, welches das E-Mail-Programm öffnet:
if ('share' in navigator) {
navigator.share({ text: 'Hello' });
} else {
window.open('mailto:?body=Hello');
}
Progressive Enhancement am Beispiel der Web Share API
Responsive
Damit Progressive Web Apps mit Angular ihr Versprechen einlösen können, vom Smartphone bis zum 4K-Bildschirm sinnvoll zu funktionieren, muss sich das Layout der Anwendung an die verfügbaren Bildschirmabmessungen anpassen. Dieses Problem wurde im Web schon lange gelöst: durch responsive Webdesign. Es gibt viele Bibliotheken, die ein responsives Layout umsetzen, etwa Bootstrap oder Foundation. Im Angular-Umfeld bietet sich außerdem Angular Material an. Diese quelloffene Bibliothek wird von Google herausgegeben und implementiert das aus Android bekannte Material Design zur Verwendung in Angular. Neben dem responsiven Layout enthält diese Bibliothek auch viele Steuerelemente, wie Datepicker oder Grids. Um Angular Material zu installieren, wird folgender CLI-Befehl ausgeführt:
ng add @angular/material
App-like
Die Eigenschaft „app-like“ besagt, dass Progressive Web Apps mit Angular so aussehen und sich verhalten sollen wie native Apps auch. Das schließt unter anderem Navigationskonzepte mit ein: Sidebars, Tabbed Interfaces oder andere bekannte Mechanismen sollen bei Progressive Web Apps zum Einsatz kommen. Wenn eine PWA aussieht wie eine normale Website, ist das Ziel verfehlt. App-like bedeutet allerdings nicht, dass PWAs zwangsläufig die native Gestaltung des Betriebssystems adaptieren müssen: Apps wie Spotify oder Slack zeigen, dass die eigene Corporate-Identity sinnvoll über verschiedene Plattformen verteilt werden kann, ohne dass die Benutzererfahrung darunter leidet.
Connectivity Independent
Progressive Web Apps wollen mit nativen Anwendungen auf Augenhöhe konkurrieren. Daher müssen sie auch dann ausgeführt werden können, wenn der Anwender gerade offline ist. Das passiert häufig: Unterwegs im Bahntunnel, im Flugzeug oder ohne Datenroaming im Ausland. Um Websites und Webanwendungen offlinefähig zu machen, wurden die Service Worker eingeführt. Dabei handelt es sich um JavaScript-Schnipsel, die durch eine Website registriert werden können.
Nach der Registrierung übernimmt der Service Worker die Kontrolle über die Website: Er erhält Zugriff auf die ausgehenden HTTP-Anfragen der Website und kann diese selbstständig beantworten. Der Service Worker hat Zugriff auf einen Cache, in dem er HTTP-Anfragen und die zugehörigen Antworten lokal zwischenspeichern kann. Dieser Cache darf nicht mit dem HTTP-Browsercache verwechselt werden, der in unveränderter Form weiter besteht. Um eine Angular-App offlinefähig zu machen, müssen ihre Quelldateien – das sind die HTML-, CSS- und JavaScript-Dateien und Assets wie Bilder oder Videos – in diesen Cache gelegt werden. Von dort aus kann der Service Worker die zwischengespeicherten Dateien selbst im Offlinezustand auslesen. Auch bei bestehender Internetverbindung hat das Vorteile: Denn in der Regel kann der Inhalt aus dem lokalen Cache deutlich schneller bezogen werden als über das Netz. Progressive Web Apps starten somit also besonders schnell.
Damit sich Entwickler nicht selbst um das Caching der Quelldateien kümmern müssen, stellt das Angular-Team eine Service-Worker-Implementierung bereit. Die Unterstützung des Projektes wurde durch die Ausführung des Konsolenbefehls vom Anfang bereits vorbereitet. Um jedoch Irritationen zu vermeiden, die sich während der Entwicklungszeit durch eine ältere, aus dem Cache ausgelieferte Anwendungsversion ergeben könnten, wird der Service Worker ausschließlich produktiven Builds der Anwendung beigefügt. Diese werden über den Befehl ng build –prod erstellt. Dieser produktive Build kann anschließend auf einen beliebigen Server hochgeladen werden.
Neben der Offlinefähigkeit der Anwendungsquelldateien beleuchtet die Eigenschaft Connectivity Independent aber auch noch einen zweiten Aspekt: Auch die strukturierten Anwederdaten, zum Beispiel Kunden- oder Artikeldatensätze, sollten offline verfügbar sein. Auch hierfür kennt das moderne Web Lösungen – konkret die lokale Browserdatenbank IndexedDB. Hier können selbst im Offlinemodus Datensätze abgelegt und beliebig modifiziert werden. Sobald die Internetverbindung wieder besteht, können die Datensätze aus der lokalen Clientdatenbank mit dem entfernten Server synchronisiert werden – also genau so, wie es native Apps auch tun würden. Allerdings bedeutet das, dass Entwickler nun die aufwendigen Themen Datensynchronisierung, Konfliktbehandlung und Datenbankmigrationen bedenken müssen. Bibliotheken wie PouchDB können allerdings dabei helfen, diese Operationen zu vereinfachen.
Fresh
Wenn die Quelldateien der Anwendung im lokalen Zwischenspeicher abgelegt werden, stellt sich die Frage, was passiert, wenn eine neue Fassung der Anwendung ausgerollt wird. Beim nächsten Aufrufen der PWA wird zunächst die im Cache hinterlegte Version geladen. Die Service-Worker-Implementierung von Angular prüft bei jedem Start der Anwendung (sofern eine Internetverbindung besteht), ob auf dem Server zwischenzeitlich eine neue Version bereitgestellt wurde. Ist das der Fall, wird die Version automatisch heruntergeladen und im lokalen Zwischenspeicher abgelegt. Beim nächsten Start der Anwendung wird dann die neue Fassung geladen.
Um den Anwender auf die Verfügbarkeit einer neuen App-Version hinzuweisen, steht in Angular der Service SwUpdate zur Verfügung. Dessen Observable available wird aufgerufen, wenn eine Aktualisierung zur Verfügung steht. Um die neue Version auszuführen, genügt es dann, die Anwendung einmal neuzuladen:
import { SwUpdate } from '@angular/service-worker';
export class AppModule {
constructor(swUpdate: SwUpdate) {
swUpdate.available.subscribe(() => {
if (confirm('Neue Version verfügbar. Neu laden?')) {
window.location.reload();
}
});
}
}
Beispielverwendung des SwUpdate-Services
Die von Angular bereitgestellte Service-Worker-Implementierung nimmt dem Entwickler sehr viel Arbeit ab, erlaubt aber nur begrenzte Konfigurationsmöglichkeiten. Wer mehr Kontrolle über den Service Worker braucht, kann das Skript auch selbst schreiben – etwa unter Zuhilfenahme der Bibliothek Workbox von Google.
Re-engageable
Service Worker können nicht nur die HTTP-Kommunikation beeinflussen, sondern auch außerhalb der Lebenszeit der eigentlichen Website ausgeführt werden. Dies erlaubt Anwendungsszenarien wie die Synchronisierung von Daten im Hintergrund oder das Senden von Pushbenachrichtigungen, ohne dass die eigentliche Webanwendung geöffnet sein muss. Beide Szenarien werden unter iOS derzeit aber nicht unterstützt. Angular erlaubt dem Entwickler, sich mithilfe des Services SwPush für die Pushkommunikation zu registrieren.
import { SwPush } from '@angular/service-worker';
export class AppComponent {
constructor(private readonly swPush: SwPush) {}
public onEnablePush() {
this.swPush.requestSubscription({
serverPublicKey: VAPID_PUBLIC_KEY
}).then(sub => this.sendToServer(sub));
}
}
Beispielverwendung des SwPush-Services
Um Pushbenachrichtigungen senden zu können, kommt das kostenfrei verwendbare Web-Push-Verfahren zum Einsatz, dessen Erläuterung den Rahmen dieses Blogposts sprengen würde. Eine umfassende Anleitung für den Betrieb mit Angular finden Sie hier.
Safe
Service Worker sind sehr mächtig: Sie erhalten Zugriff auf die HTTP-Kommunikation der Website, können stellvertretend für Webserver antworten und außerhalb der Lebenszeit der Website aktiv sein. Daher muss sichergestellt werden, dass das Service-Worker-Skript wirklich von der erwarteten Quelle stammt. Dazu muss die Website über eine gesicherte Verbindung, also über HTTPS übertragen werden. Dies ist auch für viele weitere moderne Webschnittstellen der Fall.
Aktuelle Webbrowser markieren Websites, die über HTTP übertragen werden, außerdem als „nicht sicher“. Für die Absicherung der Verbindung sind allerdings SSL- bzw. TLS-Zertifikate erforderlich, die bei Hostinganbietern wie Host Europe erhältlich sind. Darüber hinaus besteht die Möglichkeit, über die Initiative Let’s Encrypt kostenfreie, sich automatisch erneuernde Zertifikate zu beziehen. Für viele Anwendungs- und Serverplattformen stehen Bibliotheken bereit, die sich um den Bezug und die regelmäßige Aktualisierung des Zertifikates über Let’s Encrypt kümmern.
Discoverable
Weiterhin sollen PWAs „discoverable“ sein, also erkennbar und unterscheidbar von ganz normalen Websites. Somit können Suchmaschinenanbieter etwa eine eigene Kategorie für Web-Apps anbieten. Das Unterscheidungskriterium zwischen einer beliebigen Website und einer Webanwendung ist das Vorhandensein des sogenannten Web App Manifest – neben dem Service Worker die zweite PWA-Kerntechnologie. Diese Datei im JSON-Format enthält Metadaten zur Webanwendung, etwa ihren Titel, Symbole, Kategorien oder die Alterskennzeichnung. Typischerweise trägt die Datei den Namen manifest.json oder manifest.webmanifest. Auf sie verwiesen wird in der Hauptdatei der Webanwendung (typischerweise index.html) über das Link-Tag mit der Relation manifest. Durch die Ausführung des Angular-CLI-Befehls wurde dieses Tag automatisch hinzugefügt – samt einer vorgefüllten Manifestdatei und Beispielsymbolen, die das Angular-Logo zeigen.
<link rel=“manifest“ href=“manifest.webmanifest“>
Verweis auf das Web App Manifest
Installable
Neben der Offlinefähigkeit besteht der zentrale Sinn von Progressive Web Apps darin, dass sie sich auf dem Gerät des Anwenders installieren lassen. Je nach Webbrowser und Betriebssystem funktioniert das aus der Adresszeile heraus oder über das Browsermenü. Wichtig zu wissen ist allerdings, dass „Installation“ lediglich bedeutet, dass eine Verknüpfung zur Webanwendung auf dem Homebildschirm oder in der Programmliste abgelegt wird. Die Service-Worker-Features wie Offlinefähigkeit oder Pushbenachrichtigungen funktionieren auch rein im Browser, ganz ohne Installation. Für die Darstellung der Anwendung wird das zuvor genannte Web App Manifest herangezogen.
{
"name": "Meine PWA-Demo",
"short_name": "PWA-Demo",
"display": "standalone",
"start_url": "/",
"icons": [
{
"src": "assets/icons/icon-192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "assets/icons/icon-512x512.png",
"sizes": "512x512",
"type": "image/png"
}
]
}
Beispielhaftes Web App Manifest
Die Eigenschaft name definiert etwa den Namen der Anwendung. short_name kann angegeben werden, wenn der Name sehr lang ist und z.B. auf dem Homebildschirm eines Mobilgerätes abgekürzt werden soll. display gibt den Anzeigemodus an, das hier verwendete standalone bedeutet eine vollflächige Ausführung auf Mobilgeräten bzw. im eigenständigen Fenster auf Desktopsystemen. start_url definiert die URL, die beim Aufrufen des Homebildschirm- oder Programmlisteneintrags abgefragt werden soll. Hier ließe sich etwa der Anwendung per Query-Parameter mitteilen, dass gerade die installierte Variante geöffnet wurde. Schließlich können im Array icons Symbole in verschiedenen Abmessungen hinterlegt werden. Beim Installieren der Anwendung werden all diese Informationen ausgelesen und auf die Verknüpfung angewandt.
Linkable
Progressive Web Apps mit Angular sollen „verlinkbar“ sein. Es soll möglich sein, die Anwendung oder sogar einen bestimmten Anwendungszustand durch das Kopieren der URL mit einer anderen Person zu teilen. Im Web gibt es zumindest die einfache Ausbaustufe komplett geschenkt: Dazu muss einfach die URL zur Webanwendung geteilt werden. Wünschenswert ist aber auch, dass die URL bis auf einen konkreten Zielzustand verweist. Hierfür gibt es innerhalb von Single-Page-Applikationen das Routing, das die URL mit einer bestimmten Sicht der Anwendung verknüpft. Bei Angular gehört der Router zum Lieferumfang. Hierüber können Routen auf Zielzustände innerhalb der Anwendung gemappt werden.
export const ROUTES: Route[] = [
{ path: '', pathMatch: 'full', redirectTo: 'home' },
{ path: 'home', component: HomeComponent },
{ path: 'settings', component: SettingsComponent }
];
Routendefinition in Angular
Progressive Web Apps mit Angular – Fazit
Die PWA-Unterstützung von Angular erlaubt es Entwicklern, mühelos in die Welt der PWA-Entwicklung einzusteigen: Bei Progressive Web Apps mit Angular kümmert sich der Angular-Service-Worker um die Offlinefähigkeit der Anwendungsquelldateien und hält sie aktuell. Dank des mitgelieferten Web App Manifest kann die Angular-App direkt in ihrem eigenen Fenster ausgeführt werden. Manche Punkte, etwa das Responsive Design oder die Offlinefähigkeit der strukturierten Anwenderdaten verbleiben aber beim Entwickler. Am Ende lockt eine Anwendung, die mit nur einer Codebase auf allen relevanten Betriebssystemen und den Webbrowsern sinnvoll betrieben werden kann.
- Was Sie 2022 für die Entwicklung von modernen Progressive Web Apps wissen sollten - 20. Dezember 2021
- Angular-Performance: So zünden Sie den Turbo - 18. August 2020
- Progressive Web Apps mit Angular - 28. Oktober 2019