Kolumne

Die Angular-Abenteuer: Ein Blick auf Angular 5

Manfred Steyer

©SuS_Media

In der neuen Kolumne „Die Angular-Abenteuer“ stürzt sich Manfred Steyer mitten ins Getümmel der dynamischen Welt der Webentwicklung. Tipps und Tricks für Angular-Entwickler kommen dabei ebenso zur Sprache wie aktuelle Entwicklungen im JavaScript-Ökosystem.

Ein Blick auf Angular 5

Gemäß des im Dezember 2016 veröffentlichten Plans gibt es zweimal jährlich ein neues Major-Release von Angular. Wenngleich das die Möglichkeit für Breaking Changes eröffnet, sollten sich diese jedoch in Grenzen halten.

Ein Beispiel dafür ist, dass beim Umstieg auf eine neue Angular-Version auch TypeScript zu aktualisieren ist. Da aber gerade im UI-Umfeld die Zeit nicht stillsteht, hat sich das Angular-Team eine Deprecation Policy auferlegt. Demnach können mit jedem Major-Release Konzepte als veraltet erklärt werden. Die Entwickler haben dann mindestens bis zum nächsten Major Zeit, diese zu ersetzen. Auch das war bis jetzt selten Hexerei. Beispielsweise wurde die Klasse OpaqueToken durch InjectionToken<T> ersetzt, und aus dem Element template wurde ng-template, um Konflikte zu vermeiden. Teams, denen die halbjährlichen Zyklen zu kurz sind, können stattdessen auch auf Long-Time-Support-Versionen von Angular setzen. Diese werden jeweils für ein Jahr gepflegt. Die erste dieser Versionen ist 4.x. Somit wird sowohl dem Wunsch mancher Teams nach weniger Veränderung, aber auch der rasanten Weiterentwicklung von UI-Technologien Rechnung getragen. Wie auch schon beim Übergang von Version 2 auf 4 gestaltet sich der Sprung auf Angular 5 sehr evolutionär. Neue Möglichkeiten in Bereichen wie Internationalisierung, Performance oder Progressive Web-Apps runden den Leistungsumfang ab.

Internationalisierung vereinfacht

In Sachen Internationalisierung ist seit Version 4.0 einiges passiert. Beispielsweise lässt sich das Angular-Team nun durch den Autor der bekannten Bibliothek ngx-translate verstärken. Diese Bibliothek gilt im Angular-Umfeld als De-facto-Standard für das Laden von Übersetzungstexten. Der Autor hat sich vor allem um die Pipes zum Formatieren von Zahlen und Datumswerten gekümmert. Beispiele dafür sind number, currency und date. In Angular 2 und 4 basieren diese noch auf dem Internationalisierungs-API der Browser. Da dieses jedoch nicht durchgängig implementiert ist, war ein problematisches Systemverhalten die Folge. Version 5 verfolgt deswegen dieselbe Strategie wie der Vorgänger AngularJS: Es existiert nun pro Kombination aus Sprache und Land, kurz Locale genannt, eine TypeScript-Datei mit Metadaten. Diese sind natürlich nicht von Hand geschrieben, sondern werden aus dem Unicode-Repository generiert. Um die Bundle-Größe nicht unnötig anwachsen zu lassen, muss die Anwendung die gewünschten Dateien zunächst importieren. Danach kann eine dieser Locales als Standard festgelegt werden. Möchte die Anwendung davon abweichen und auf ein anderes der eingebundenen Locales wechseln, kann sie das damit einhergehende Kürzel an den letzten Parameter der Pipes übergeben.

Die Standardsprache lässt sich hingegen nicht im Nachhinein ändern. Auch wenn das auf den ersten Blick seltsam erscheint, gibt es dafür gute technische Gründe. Es geht dabei um das wohl wichtigste Architekturziel von Angular: Performance. Um die Handhabe der Pipes zu optimieren, sieht Angular das Konzept der puren Pipes vor. In Anlehnung an die Welt der funktionalen Programmierung sind das Pipes, deren Ausgaben einzig und allein von ihren Eingaben bestimmt werden. Sich ändernde Nebeneffekte, wie eine globale Variable mit der Standard-Locale, dürfen sich auf solche Pipes nicht auswirken. Aufgrund dieser Einschränkung muss Angular pure Pipes auch nur erneut ausführen, wenn sich die Eingaben ändern. Nicht-pure Pipes, die von Nebeneffekten abhängen, muss Angular hingegen immer und immer wieder ausführen, konkret nach jedem Ereignis. Zumal es die Nebeneffekte nicht überwachen kann. Das ist auch der Grund, warum die meisten von Angular gelieferten Pipes pur sind. Und das betrifft auch die hier genannten. Ein Beispiel für das Registrieren von Locales findet sich in Listing 1. Es importiert die gewünschten Metadaten und registriert sie mit der Funktion registerLocaleData.

import { registerLocaleData } from '@angular/common';
import localeDe from '@angular/common/locales/de';
import localeDeAt from '@angular/common/locales/de-AT';
import localeEs from '@angular/common/locales/es';

registerLocaleData(localeDe);     // de-DE
registerLocaleData(localeDeAt);   // de-AT
registerLocaleData(localeEs);     // es-ES

Die Standardsprache legt das hier gezeigte Beispiel für den JIT-Modus mit einem Provider für das Token LOCALE_ID auf Deutsch fest:

providers: [
  [...]
  { provide: LOCALE_ID, useValue: 'de' },
]

Für AOT-Builds kann dieselbe Information über einen Kommandozeilenparameter angeführt werden:

ng build --prod --locale=de

Die Formatierung eines Datums mit den importierten Locales übernimmt es in weiterer Folge wie folgt:

<p>Datum (Default=de): {{item.date | date:'long'}}</p>
<p>Datum (de-AT): {{item.date | date:'long':'': 'de-AT'}}</p>
<p>Datum (es): {{item.date | date:'long':'': 'es'}}</p>

Während beim ersten Aufruf die Standard-Locale zum Einsatz kommt, legen die beiden anderen Aufrufe eine davon abweichende Locale über den letzten Parameter fest.

Entfernen von Whitespaces

Zur weiteren Verbesserung der Performance entfernt der Compiler auf Wunsch nun unnötige Leerzeichen aus den Templates und den JavaScript-Dateien, die daraus kompiliert werden. Der erzielte Effekt ist größer, als man annehmen möchte. Als Anschauungsbeispiel soll das folgende HTML-Fragment dienen:

<p>Hallo      </p>
<p>      Welt!</p>

Durch das Entfernen der Leerzeichen nach dem Hallo, die der Browser ohnehin nicht darstellt, gewinnt man zunächst nur ein paar Byte. Ähnlich ist es bei den Leerzeichen vor Welt!. Der Browser stellt nur eines davon dar, somit können auch hier die restlichen entfernt werden. Am meisten bringt jedoch das Entfernen des Textknotens, der sich im entstehenden DOM zwischen dem schließenden Element am Ende der ersten und dem öffnenden Element am Beginn der zweiten Zeile befindet. Beim Kompilieren der Templates entsteht nämlich für jeden DOM-Knoten ein Funktionsaufruf, und genau der kann hier entfallen. Im gezeigten Beispiel fällt somit einer von drei Knoten weg. Abseits solcher Schulhofbeispiele kann man damit – natürlich abhängig von der Anwendung – rund zehn Prozent der Bundle-Größe einsparen. Das wirkt sich auch merkbar auf die Startgeschwindigkeit aus. Um in den Genuss dieser Optimierungsoption zu kommen, ist über den Component-Decorator die Option preserveWhitespaces auf false festzulegen:

@Component({
  selector: 'flight-card',
  templateUrl: './flight-card.component.html',
  preserveWhitespaces: false
})
export class FlightCardComponent {...}

Außerdem ist hierfür eine globale Einstellung vorgesehen, die sich auf alle Komponenten auswirkt. Als dieser Text geschrieben wurde, stand sie jedoch noch nicht zur Verfügung.

AOT als Standard?

Die Templatekompilierung führt Angular entweder beim Start der Anwendung im Browser oder im Build aus. Ersteres nennt sich Just in Time Compilation (JIT), Letzteres Ahead of Time Compilation (AOT). Es liegt auf der Hand, dass AOT zur besseren Startperformance führt. Aus diesem Grund ist es üblich, dass AOT für Production Builds zum Einsatz kommt. Während der Entwicklung kommt hingegen derzeit meist JIT zum Einsatz, da die AOT-Kompilierung in der Regel zu langsam ist. Leider ist Angular im AOT-Modus auch etwas strenger. Das führt dazu, dass bei der AOT-Kompilierung zusätzliche Fehler entdeckt werden können. Deswegen hat sich das Angular-Team bereits im April 2017 auf der ng-conf in Salt Lake City dafür ausgesprochen, dass AOT so schnell werden soll wie JIT. Einige Check-ins in deren GitHub-Repository deuten auch schon in diese Richtung. Ob es tatsächlich mit Angular 5 oder erst danach, z. B. mit einer Minor-Release, soweit ist, wird sich im Laufe der nächsten Wochen zeigen. Als Alternative bietet sich heute schon der Angular Service an: Eine Erweiterung für IDEs, die bereits beim Tippen die Prüfungen des AOT-Compilers durchführt und ganz nebenbei Codevorschläge auch in HTML-Templates ermöglicht.

Lesen Sie auch: TypeScript Tutorial: Grundlagen und Typisierung für JavaScript

ngUpgrade Lite macht Parallelbetrieb schneller

Die Bibliothek ngUpgrade erlaubt seit den ersten Tagen von Angular einen Parallelbetrieb des alten AngularJS 1.x mit dem neuen Angular ab Version 2 und ist somit ein Schlüssel für eine schrittweise Migration. Durch das Bereitstellen von Wrappern kann eine Anwendung Services und Komponenten verschiedener Versionen miteinander kombinieren. Damit eine Verschachtelung funktioniert, muss das Change Tracking von AngularJS 1.x jenes von Angular anstoßen und vice versa. Dass das zu Performanceproblemen führen kann, liegt auf der Hand. Genau in diese Kerbe schlägt ngUpgrade Lite. Hinter diesem Namen verbirgt sich ein leichtgewichtiger Betriebsmodus, der die Change Tracker nicht verschränkt und somit für eine gute Performance sorgt. Dieses Vorgehen ist ideal für Anwendungen, in denen ein Teil komplett mit AngularJS 1.x und andere, neuere Teile komplett mit Angular umgesetzt sind. In den hoffentlich seltenen Fällen, in denen sich eine Änderung auf beide Bereiche auswirkt, ist das Change Tracking manuell anzustoßen. Eine Einführung zu dieser neuen Spielart von Angular-Vater Miško Havery findet sich auf youtube.

Server-side Rendering: Nicht zweimal laden

Das serverseitige Vorrendern der ersten anzuzeigenden Seite stellt sicher, dass der Benutzer möglichst rasch eine erste Ansicht erhält. In diesem Fall bekommt er sofort nach dem Herunterladen der index.html Inhalte präsentiert und muss nicht warten, bis die JavaScript Bundles auch noch geladen und ausgeführt wurden. Daneben erleichtert diese Maßnahme die Indizierung durch Suchmaschinen. Da dieser Betriebsmodus vor allem für Consumer-Anwendungen strategisch wichtig ist, hat ihn das Angular-Team mit Version 4 ins Framework integriert. Davor war er in Form eines Communityprojekts verfügbar. Allerdings reicht es hier nicht, nur den Betriebsmodus zu wechseln, zumal serverseitig andere Gesetzmäßigkeiten vorherrschen. Beispielsweise kann am Server nicht auf das DOM des Browsers zugegriffen werden. Eine Alternative ist der Renderer von Angular. Er abstrahiert das DOM und führt abhängig von der aktuellen Umgebung die passenden Aktionen aus, z. B. DOM-Manipulationen im Browser oder das Erzeugen von Strings mit HTML-Elementen am Server. Leider greifen noch viele Bibliotheken von Drittanbietern direkt auf das DOM zu. Das ist beispielsweise bei der großen Schar an zur Verfügung stehenden jQuery-Plug-ins der Fall. Um das Zusammenspiel zu vereinfachen, kommt nun serverseitig die DOM-Simulation Domino zum Einsatz.

Eine weitere Herausforderung bei Server-side Rendering ist das Übertragen von Zuständen vom Server zum Client. Damit möchte man verhindern, dass dieselben Daten auf beiden Seiten abgefragt werden müssen. Während sich Entwickler darum bis dato selbst kümmern mussten, können sie nun auf das neue Transfer-API zugreifen. Aus Sicht des Entwicklers handelt es sich hierbei lediglich um ein Dictionary, das sich serverseitig befüllen und clientseitig auslesen lässt. Das Communityprojekt nguniversal bietet unter anderem eine Erweiterung, die das Transfer-API mit dem HttpClient verbindet. Somit nähren sich Abfragen, die clientseitig wiederholt werden, automatisch aus diesem Dictionary. Damit das Transfer-API funktioniert, ist clientseitig das Angular-Modul BrowserTransferStateModule aus dem Namensraum @angular/platform-browser sowie auf der Serverseite das Angular-Modul ServerTransferStateModule aus @angular/platform-server zu importieren.

Service Worker: Wer wann mit dem Cache darf

Progressive Web-Apps (PWA) sind derzeit in aller Munde, erlauben sie doch offlinefähige Webanwendungen mit nativen Eigenschaften wie Push-Nachrichten. Eine Kerntechnologie dafür sind Service Worker, die als Hintergrundprozesse im Browser laufen und sich ums Caching von Programmdateien, aber auch abgefragten Daten kümmern. Im Gegensatz zum klassischen Browsercache kann die Anwendung über den Service Worker direkt entscheiden, wann welche Informationen aus dem Cache kommen und wann der Cache zu aktualisieren ist. Mit einem High-Level-API möchte das Angular-Team die Arbeit damit erleichtern. Eine erste interne Version lag schon zu Zeiten von Angular 4.3 vor. Damit hat das Team auch die eigene Webseite erweitert. Die dabei gewonnen Erkenntnisse sind in die erste öffentliche Version eingeflossen, die ab Angular 5 zur Verfügung steht. Dieses neue API stellt einen generischen Service Worker zur Verfügung, der sich über eine JSON-Datei konfigurieren lässt. Soweit es geht, wird die Konfiguration jedoch aus bereits bekannten Informationen abgeleitet, z. B. die Namen der generierten Bundles. Zusätzlich bereitgestellte Services erleichtern den Empfang von Push-Nachrichten oder das programmatische Update des Cache.

Um dieses Service-Worker-API zu verwenden, ist zunächst eine Konfigurationsdatei bereitzustellen, die das Caching-Verhalten der Anwendung beschreibt. Aus dieser Konfigurationsdatei wird eine umfangreichere Konfigurationsdatei generiert, die z. B. alle beim Build erzeugen Dateien listet. Anschließend muss nur noch das ServiceWorkerModule importiert werden. Über weitere Services lässt sich das Verwalten zum Aktualisieren des Cache sowie zum Empfang von Push-Nachrichten steuern. Eine Einführung in dieses neue API findet sich hier.

Fazit

Angular 5 bringt einige nette Abrundungen, die bestimmte Szenarien wie Server-side Rendering, Internationalisierung oder Progressive Web-Apps unterstützen. Große Breaking Changes bleiben aus. Stattdessen greift das Angular-Team auf die selbst auferlegte Deprecation Policy zurück. Das war auch schon beim Übergang von Version 2 auf 4 so und ist ein schönes Zeichen seitens des Angular-Teams. Es zeigt, dass man bemüht ist, auf Stabilität zu achten, ohne sich die Möglichkeit nehmen zu lassen, neue Features zu liefern. Da mit jedem Angular-Release auch zahlreiche kleinere Features einhergehen, gibt es hier ausdrücklich keinen Anspruch auf Vollständigkeit. Wer sich die hier verwendeten Beispiele etwas genauer ansehen möchte, findet sie auf meiner GitHub-Seite.

LESEN SIE AUCH:

Angular 2 Tutorial: Moderne Frontends für alle Plattformen entwickeln

Geschrieben von
Manfred Steyer
Manfred Steyer
Manfred Steyer ist selbstständiger Trainer und Berater mit Fokus auf Angular 2. In seinem aktuellen Buch "AngularJS: Moderne Webanwendungen und Single Page Applications mit JavaScript" behandelt er die vielen Seiten des populären JavaScript-Frameworks aus der Feder von Google. Web: www.softwarearchitekt.at
Kommentare

Hinterlasse einen Kommentar

Hinterlasse den ersten Kommentar!

avatar
400
  Subscribe  
Benachrichtige mich zu: