Steve Jobs war seiner Zeit weit voraus. Als er vor zehn Jahren, im Jahr 2007, das erste iPhone vorstellte, hatte er eine Vision, wie Apps funktionieren sollen. Nach seiner Vorstellung sollten Web-Apps komplett im Safari-Browser laufen und per JavaScript und Ajax das Verhalten von nativen Apps imitieren. Der Plan ging jedoch nicht auf. Das erste iPhone war für Web-Apps zu langsam, viele Schnittstellen zur Hardware fehlten, und die User Experience war den nativen Apps massiv unterlegen. Entwickler fühlten sich von Apple bevormundet und forderten die Möglichkeit, native Apps zu schreiben. Mit den daraus resultierenden App Stores entstand ein Markt mit milliardenschweren Umsätzen. Zehn Jahre später sind Apps ein fester Bestandteil von iOS, Android und Windows Phone. Dennoch gibt es einen Trend, der die Idee der ursprünglichen Web-Apps aufgreift und in ein modernes Gewand packt.
Viele Gründe, die Web-Apps vor zehn Jahren scheitern ließen, sind heute nicht mehr aktuell. Smartphones haben heutzutage die Rechenleistung von Desktopcomputern, und Standards wie HTML5, CSS3 und JavaScript haben sich deutlich weiterentwickelt. Im Jahr 2015 prägte Google den Begriff Progressive Web-Apps (PWAs). PWAs sind Webanwendungen, die sich wie Apps anfühlen, aber über den Browser und ohne den Umweg über die App-Store-Installation aufgerufen werden können. Google hat genaue Vorstellungen, was eine Webseite zur Progressive Web-App macht. PWAs sollen sich für Nutzer wie native Apps anfühlen: Sofortiges Laden von Inhalten ohne spürbare Verzögerung, Offlinefähigkeit und die Möglichkeit, die App auf dem Homescreen zu installieren. Es geht sogar noch weiter. Progressive bedeutet in diesem Kontext, dass eine grundlegende Version der Seite auch auf Browsern funktioniert, die nicht die modernsten Standards unterstützen. Somit schließen PWAs die Lücke zwischen nativen Apps und mobilen Seiten. Für ältere Browser wird die Seite zumindest rudimentär angezeigt. Unterstützt ein Browser alle notwendigen Features, wird dem Nutzer ein App-ähnliches Erlebnis geboten. Um dieses Ziel umzusetzen, sind einige neue Konzepte notwendig, die im Folgenden besprochen werden.
Service Worker
Service Worker sind lichtscheu. Als Nutzer einer Web-App hat man normalerweise keinen direkten Kontakt zu ihnen. Dennoch sind sie ein wichtiger Teil der Magie, die dafür sorgt, dass eine PWA offlinefähig ist oder Notifications anzeigen kann. Um Service Worker weiter zu entzaubern: Sie sind schlichtweg JavaScript-Programme, die im Browser laufen.
Argwöhnische Leser mögen nun fragen, wie sich Service Worker von normalen Skripten auf Webseiten unterscheiden. Der größte Unterschied liegt im Kontext der Service Worker: Es handelt sich um globale Skripte, die nicht an eine bestimmte Seite gebunden sind. Sie gelten generell für einen gesamten Ressourcenpfad einer Domain und können auch ohne eine Webseite ausgeführt werden. Durch den globalen Skriptkontext haben sie zudem keinen Zugriff auf das DOM (Document Object Model) der jeweiligen Seiten. Ganz bildlich kann man sich Service Worker als Schnittstelle zwischen der Webseite und dem Internet vorstellen. Derzeit spielen vor allem die drei folgenden Aspekte eine besondere Rolle.
Offlinefähigkeit
Service Worker können Ressourcenaufrufe der jeweiligen Seite übernehmen und durch gecachte oder modifizierte Responses ersetzen. Dem sicherheitsorientierten Leser mag es beim Gedanken an durch Dritte modifizierte Responses nun kalt den Rücken hinunterlaufen. Doch keine Sorge, um Service Worker einzusetzen, ist HTTPS Pflicht, um eventuelle Man-in-the-Middle-Angriffe zu verhindern.
Background-Sync
Stellen Sie sich folgende Situation vor: Sie nutzen den Webmailclient ihres E-Mail-Anbieters und tippen auf dem Smartphone eine lange E-Mail. Just in dem Moment, in dem Sie auf Absenden drücken, verliert Ihr Smartphone die Mobilfunkverbindung und sowohl Ihre eingegebene E-Mail als auch Ihre gute Laune sind verschwunden. Mit einer Background-Sync-Implementierung wäre das nicht passiert. Background-Sync erlaubt es, Aktionen dann auszuführen, wenn das Netzwerk verfügbar ist. Das funktioniert sogar, wenn der Browser-Tab geschlossen oder der Anwender längst zu einer anderen Seite navigiert ist.
Push Notifications
Wird eine Webseite gerade nicht im Smartphonebrowser angezeigt, kann sie nicht mit dem Nutzer interagieren. Diese einfache wie offensichtliche Erkenntnis führt dazu, dass für Hinweismeldungen ein globaler Skriptkontext notwendig ist. Genau den bieten Service Worker. Mit dem Notifications-API ist es möglich, dem Anwender sowohl auf dem Desktopbrowser als auch auf dem Smartphone Benachrichtigungen anzuzeigen.
Wie progressiv soll es sein?
Um den Entwicklern das Leben zu erleichtern und die Qualität von PWAs insgesamt zu steigern, bietet Google mit Lighthouse ein Open-Source-Werkzeug an, mit dem man eine Webseite auf Herz und Nieren hinsichtlich der Anforderungen von PWAs prüfen kann. Lighthouse ist als Chrome Extension über den Chrome Web Store oder als Kommandozeilentool über npm install -g lighthouse verfügbar. Somit kann es direkt in Webpack Builds eingebunden werden, um regelmäßiges Feedback zu erhalten.
Lighthouse bietet von Haus aus einen Satz an Metriken, mit dem es eine Untersuchung für eine gegebene Seite durchführt. Zu diesen Metriken gehört unter anderem der Test, ob die Seite Inhalte bei nicht vorhandener Internetverbindung anzeigt oder wie sie mit schwankender Leitungsgeschwindigkeit umgeht. Auch die Zeit, die für das Rendern vergeht, bis der Nutzer etwas Sinnvolles angezeigt bekommt, wird von Lighthouse gemessen. Features wie Secure Origin und Barrierefreiheitsaspekte werden ebenfalls getestet. Das Testergebnis zeigt Lighthouse auf einer übersichtlichen HTML-Seite oder als JSON-Datei an. Die Analyse wird in verschiedene Kategorien mit einem jeweiligen Score von 0 bis 100 aufgeschlüsselt. Die Kategorien umfassen derzeit:
- Progressive Web-App: Service-Worker-Test, Offlinefähigkeit, Ladegeschwindigkeit unter 3G-Bedingungen, Anbieten eines Splash-Screens, Einfärben der Adresszeile in den Farben der Seite)
- Performance: diverse Metriken zur Renderzeit
- Barrierefreiheit: Annotationen für Bilder und Links, damit Screen-Reader sie vorlesen können
- generelle Best Practices: Verwendung von HTTP2, keine direkte Nachfrage nach der Location des Nutzers oder ob Benachrichtigungen angezeigt werden dürfen
Der Report gibt für jeden einzelnen Analysepunkt Hinweise und Tipps, wie der Score verbessert werden kann. Mit diesem Rüstzeug gewappnet, können wir uns nun mit der Frontend-Technologie React beschäftigen, um damit eine PWA zu entwickeln.
React
Die Frontend-Bibliothek React erblickte 2013 als Open-Source-Bibliothek das Licht der Welt. Damals setzte Facebook massiv auf den Einsatz einer modifizierten PHP-Erweiterung namens XHP. Ein Software Engineer bekam die Erlaubnis, XHP im Browser in JavaScript zu implementieren, um das Problem der performancehungrigen Roundtrips des serverseitig erzeugten HTML-Codes zu entschärfen: React war geboren.
Vier Jahre später ist React eine der beliebtesten Frontend-Bibliotheken. Viele namhafte Webseitenbetreiber wie Instagram, Netflix, Imgur oder Walmart setzen auf die Oberflächenbibliothek. React bietet von Haus aus nur User Interfaces. Somit deckt die Bibliothek nur den View-Teil im Model-View-Controller-Pattern ab. Das hat den großen Vorteil, dass keine Architektur vorgeschrieben ist und man React auch sehr flexibel in bestehende Anwendungen integrieren kann. Zudem gibt es eine riesige Auswahl an zusätzlichen Bibliotheken und Frameworks, um React zu einem vollwertigen Paket anzureichern.
Frameworkless – the new black?
by Carsten Windler (KW-Commerce GmbH)
Getting started with PHP-FFI
by Thomas Bley (Bringmeister GmbH)
JSON-Schema von 0 auf 100
by Ingo Walther (Electronic Minds GmbH)
Interview mit Christian Schneider zum Thema „DevSecOps“
DevSecOps ist, bezogen auf Security-Checks, die logische Fortführung der Automatisierung im DevOps-Sinne
IT-Security Experte, Christian Schneider
Christian ist als freiberuflicher Whitehat Hacker, Trainer und Security-Coach tätig. Als Softwareentwickler mit mittlerweile 20 Jahren Erfahrung, fand er 2005 seinen Themenschwerpunkt im Bereich IT-Security.
React verfolgt einen komponentenorientierten Ansatz. Der von React propagierte Ansatz ist es, das UI einer Seite in unabhängige und wiederverwendbare Einheiten zu schneiden. Diese Komponenten stehen in Eltern-Kind-Beziehungen zueinander. Ein besonderes Schmankerl nennt sich Virtual DOM. Das Document Object Model ist die baumstrukturierte Repräsentation eines HTML-Dokuments. Möchte man im HTML-Dokument beispielsweise einen Zähler in einem Textfeld hochsetzen, muss dafür das DOM geändert werden. Bei komplexen Webseiten ist das Neuschreiben des gesamten DOM eine zeitintensive Angelegenheit. Dank des Virtual DOM verändert React nur die Teile des DOM, die sich wirklich geändert haben. Dadurch können Webanwendungen extrem performant im Browser ausgeführt werden.
Performance ist jedoch nur ein Aspekt, auf den die React-Schöpfer Wert legen. Es geht auch darum, gut lesbaren und verständlichen JavaScript-Code zu schreiben. Aus diesem Grund verfolgt React einen deklarativen Ansatz. Als Entwickler beschreibt man, wie eine Komponente funktionieren und aussehen soll. Die React-Bibliothek übernimmt anhand dieser Beschreibungen das Instanziieren von JavaScript-Objekten. Um die Lesbarkeit weiter zu steigern und damit sich Webentwickler wie zu Hause fühlen, hat Facebook eine JavaScript-Erweiterung namens JSX veröffentlicht. JSX ermöglicht es, XML-Fragmente direkt im JavaScript-Code zu verwenden. Somit kann die HTML-Syntax direkt im JavaScript-Code genutzt werden und es kommt einem vor, als würde man HTML schreiben, obwohl man gerade in React entwickelt. Listing 1 zeigt eine einfache Komponente für eine statische To-do-Liste.
class ToDoComponent extends React.Component { render() { return ( <div className="todo-list"> <h1>ToDo-Liste für {this.props.name}</h1> <ul> <li>Einkaufen</li> <li>Putzen</li> </ul> </div> ); } }
Im JavaScript-Code würde die Komponente folgendermaßen benutzt werden:
<ShoppingList name="Heinz" />
Kritische Zeitgenossen mögen nun fragen, wie der Name Heinz in die Komponente kommt und was das props-Objekt damit zu tun hat. Im Gegensatz zu Angular – dort gibt es Two-Way Data Binding – setzt React auf das Prinzip des One-Way Data Flow. Daten fließen immer unidirektional von ihren Erzeugern zu den Empfängern. React unterteilt die Daten einer Komponente in zwei unterschiedliche Kategorien: State und Props (kurz für Properties).
React-Anfänger tun sich am Anfang manchmal schwer damit, diese auseinanderzuhalten. Properties sind Daten, die sich innerhalb der Komponente nicht ändern und die von außen zugeführt werden. Der State enthält Daten, die sich im Lebenszyklus einer Komponente verändern können. Soll eine Komponente beim Rendern beispielsweise stets eine bestimmte Hintergrundfarbe haben, könnte das über eine Property abgebildet werden. Soll der Farbcode über ein Textfeld verändert und gespeichert werden, würde hierfür der State verwendet werden. Ein weiteres Prinzip für die Datenhaltung nennt sich Data-down-Actions-up. Dieses Prinzip erklärt den Datenfluss innerhalb einer Hierarchie von Komponenten. Daten fließen stets von Elternkomponenten zu Kindkomponenten. Möchten die Kindkomponenten jene Daten abändern, tun sie das nicht selbstständig, sondern lösen Actions aus, die die Änderung der Daten in den Elternkomponenten veranlassen.
Create-React-App
Wer neu im JavaScript-Universum ist, verzweifelt am Anfang schnell an der schieren Masse an Tools und deren Konfigurationsdateien. React-Neulinge scheitern oft schon am Aufbau des Set-ups mit Webpack, Babel und Konsorten, bevor sie auch nur eine Zeile React-Code schreiben. Anfangs stellte die Community Skripte zur Verfügung, um das initiale Aufsetzen eines React-Projekts zu erleichtern. Mittlerweile bietet Facebook dafür auch ein offizielles Werkzeug an. create-react-app soll die Erstellung und Konfiguration von React Apps erleichtern und unterstützen. So kann mit einem Terminalbefehl eine vollständige Web-App erzeugt werden. Das Tool lässt sich über den Node-Befehl npm install -g create-react-app installieren.
Mit dem Befehl create-react-app todo-liste legen wir ein einfaches React-Basisprojekt an. Das erzeugte Projekt bringt bereits die komplette Build-Konfiguration mit. Somit kann man sich vor allem zu Beginn voll auf React konzentrieren. Besonders nützlich ist, dass create-react-app bereits eine umfangreiche Unterstützung für Progressive Web-Apps mitbringt. So wird im public-Ordner des Projekts automatisch eine Manifestdatei erzeugt und mit Standardangaben über Icons, Farbthemen und Bezeichner der App gefüllt. Wer nicht jetzt schon vor Glück jubelt, wird über eine weitere Sache entzückt sein: Im src-Ordner des Projekts erzeugt create-react-app automatisch ein Service-Worker-Skript namens registerServiceWorker.js und registriert es in der Datei index.js. Dieses Skript ist nur im Produktionsmodus aktiv und übernimmt das Caching und Ausliefern der Assets der Web-App. Damit ist unsere App in unterstützten Browsern wie Chrome und Firefox schon offlinefähig. Mit den Kommandos cd todo-liste und npm start startet die Webanwendung im Development-Modus und kann im Browser unter http://localhost:3000/ aufgerufen werden (Abb. 1).

Abb. 1: Die von create-react-app erzeugte Basisanwendung
Erstellung einer einfachen To-do-Listen-Anwendung in React
Wir beginnen mit der ziemlich unspektakulären Beispielanwendung, die create-react-app erzeugt hat, und erweitern sie zu einer einfachen To-do-Listen-Anwendung. Ziel soll sein, dass Einträge über ein Textfeld zu der To-do-Liste hinzugefügt werden. Gemäß der React-Denkweise beginnen wir nun, die Anwendung in verschiedene Komponenten zu zerteilen. Die erste Komponente besteht aus dem Textfeld und dem Button, mit dem neue Einträge zur Liste hinzugefügt werden. React bietet verschiedene Wege an, um Komponenten zu definieren: Functional Components und Class Components. Eine Functional Component ist schlichtweg eine JavaScript-Function, die eine React-Komponente beschreibt. Class Components nutzen ECMAScript-2015-Klassen, um Komponenten zu beschreiben, und bieten mehr Möglichkeiten in Bezug auf den State und den Lebenszyklus der Komponente. Für die Eingabekomponente der To-do-Liste verwenden wir eine Class Component, da wir im State die Einträge unserer Liste speichern werden. Die Klasse TodoList fügen wir unterhalb der Importanweisungen in die von create-react-app generierte Datei App.js im Ordner src ein (Listing 2).
class TodoList extends React.Component { render() { return ( <div className="todoListMain"> <div className="header"> <form> <label> Zu erledigen: <input ref={input => (this._inputElement = input)} /> </label> <button type="submit"> Hinzufügen </button> </form> </div> </div> ); } }
Wer nun erneut die Adresse http://localhost:3000 öffnet, wird sich verwundert die Augen reiben. Wir sehen immer noch die Demostartseite, an der sich nichts geändert hat. Aus gutem Grund. Wir haben zwar eine Komponente definiert und beschrieben, wie sie gerendert werden soll. An welcher Stelle das geschehen soll, haben wir React allerdings nicht mitgeteilt.
Das lässt sich schnell nachholen, in dem wir die Klasse App so ändern, dass sie unsere TodoList-Komponente benutzt. Dazu können wir beispielsweise die Codestelle <p className=“App-intro“> in der Render-Funktion der Klasse App folgendermaßen verändern:
<div className="App-intro"> <TodoList /> </div>
Der Browser sollte die veränderte Seite nun dank des von create-react-app konfigurierten Hot Reloadings automatisch neu laden. Hinzufügen lassen sich die Einträge für die To-do-Liste jedoch noch nicht. Dazu benötigen wir einen Ablageort, an dem wir die Einträge für die To-do-Liste speichern. Hierfür eignet sich der State der Komponente. Somit definieren wir in der Klasse TodoList zuerst ein leeres items-Objekt state (Listing 3).
state = { items: [] }; Nun erstellen wir in TodoList eine Funktion, die beim Klick auf den Hinzufügen-Button ausgelöst werden soll. addItem = e => { const itemArray = this.state.items; itemArray.push({ text: this._inputElement.value, key: Date.now() }); this.setState({ items: itemArray }); this._inputElement.value = ""; e.preventDefault(); };
Damit die Funktion beim Abschicken des Formulars ausgelöst wird, muss der <Form>-Tag in der Klasse TodoList noch entsprechend geändert werden:
<form onSubmit={this.addItem}>
Die Funktion addItem erzeugt ein Objekt mit den Attributen text und key. Der key ist wichtig, da React sonst mehrere Listeneinträge nicht eindeutig voneinander unterscheiden kann. Der setState-Aufruf sorgt offensichtlich dafür, dass React die übergebenen Objekte im State der Komponente ablegt. Dabei geht das Framework äußerst clever vor und sammelt asynchron eventuell erst mehrere dieser Aufrufe, um den State zu ändern. Aufmerksame Leser mögen nun die Frage stellen, woher _inputElement stammt. Tatsächlich haben wir es schon im Inputelement des Formulars der Klasse TodoList definiert:
ref={input => (this._inputElement = input)}