Frontend Build @ Chrono24

Junior Frontend Developer @ Chrono24

Bei Chrono24 entwickeln wir nicht nur unsere Plattform ständig weiter, sondern arbeiten auch stetig an unserem Tooling, um Build- und Deployment-Prozesse möglichst effizient und modern zu gestalten. In den letzten Jahren sind reihenweise Open-Source-Tools zur Automatisierung von Frontend Builds aus dem Boden geschossen, die natürlich auch wir zur Unterstützung unserer Prozesse einsetzen.

Da wir aktuelle Technologien schätzen und sich einige dieser Tools bereits etabliert haben, befindet sich unser Toolkit ständig im Wandel — von maßgeschneiderter Custom-Software hin zu Standard-Tools, auf die auch viele digitale Riesen wie Google, Adobe oder Slack vertrauen.

Wozu brauchen wir den Frontend Build?

Das Frontend einer Webanwendung besteht eigentlich nur aus 4 Teilen: dem HTML-Dokument, Stylesheets, Skripten und Assets wie Bildern oder Schriftarten. Trotzdem wird in der modernen Webentwicklung aus verschiedenen Gründen ein Build-Prozess benötigt.

Zum einen möchte man in der Lage sein, seinen Code zu splitten und so nicht ein einziges, unter Umständen sehr großes, CSS- oder Javascript-File pflegen zu müssen. So lässt sich dann der Code in einzelne Komponenten oder Module aufteilen, die für ihren Teil wiederverwendbar und gekapselt sind.

Gleichzeitig wird es schnell unübersichtlich, wenn in einem HTML-Dokument sehr viele Stylesheets oder Skripte geladen werden. Außerdem erhöht sich die Ladezeit enorm, falls ein Browser ohne HTTP/2-Unterstützung verwendet wird, weil dann für jeden Request eine eigene TCP-Verbindung aufgebaut werden muss.

Das Hauptargument für einen Build-Prozess ist aber die Verwendung von modernen Standards, die ohne Build-Prozess ein manuelles Transpilieren oder Kompilieren von Source Code verlangen. In der modernen Webentwicklung sind ECMAScript und CSS-Präprozessoren nicht mehr wegzudenken. Allerdings wird der ES6-Standard von älteren Browsern nicht unterstützt und das CSS aus Stylesheet-Sprachen wie Sass, Less oder Stylus zur Laufzeit generieren zu lassen ist oft wenig performant. Auch externe Bibliotheken lassen sich mit Hilfe von Build-Tools bequem importieren.

Modernes Javascript (ES6) bietet eine Vielzahl neuer Features und Shortcuts, die von alten Browsern nicht interpretiert werden können. Die nachfolgende Grafik zeigt die Feature-Unterstützung der einzelnen Browser. Der Internet Explorer unterstützt nur 11% aller neuen ES6-Funktionen.

Browser-Kompatibilität von ES6-Features
Browser-Kompatibilität von ES6-Features

 

Der Internet Explorer ist in Deutschland mit ca. 4% Marktanteil so populär, dass er bei der Entwicklung von Webanwendungen noch nicht zu vernachlässigen ist, wie das folgende Schaubild zeigt. Bei über 500.000 Besuchern täglich sind auch bei Chrono24 sehr viele IE-User unterwegs.

Marktanteile von Desktop-Browsern in Deutschland
Marktanteile von Desktop-Browsern in Deutschland

Teilbereiche des Build-Prozesses

In der modernen Webentwicklung gibt es viele Möglichkeiten, Tools und Compiler einzusetzen, um Code effizienter schreiben, pflegen und verwalten zu können. Das Ziel der Tools lautet häufig Code Splitting. Die Vorteile von Kapselung und Modularisierung liegen klar auf der Hand: Austauschbare Module, Wiederverwendbarkeit von Code, klare Aufgabenteilung zwischen den Komponenten oder die Möglichkeit, Komponenten nur dann zu laden, wenn sie auch benötigt werden. Diese Liste zeigt nur einen Bruchteil der Vorteile von komponentenbasierter Code-Struktur und lässt sich fast beliebig erweitern.

HTML-Templates

Auch für HTML-Dokumente selbst gibt es viele Fälle, in denen Templates oder Snippets ausgelagert werden sollten. Leider sieht die HTML-Spezifikation keine native Unterstützung zur Komposition von HTML-Dokumenten oder Templates vor. Inzwischen wurde sogar die Unterstützung von HTML imports in Google Chrome entfernt. Daher ist die einzige Möglichkeit, die Vorteile von HTML-Modulen zu nutzen, eine Template Engine wie Handlebars zu verwenden oder auf ein Framework wie Vue, Angular oder React zu setzen. Alternativ kann auch auf SSR (Server Side Rendering) gesetzt werden, da Technologien wie PHP oder JSP das Importieren von Markup erlauben.

Assets

Viele Webseiten und Webanwendungen nutzen sehr viele kleine Bilder und Logos, wobei mit Verwendung von HTTP 1.1 für jede Datei eine eigene TCP-Verbindung zwischen Server und Client aufgebaut werden muss. Um die Ladezeiten zu beschleunigen, wird daher häufig ein Sprite Sheet erzeugt. Dabei werden alle kleinen Bilder zu einem großen Bild zusammengefügt und der jeweilige Bildausschnitt für jedes Einzelbild per CSS definiert. Dadurch muss nur noch ein Request ausgeführt und damit nur eine TCP-Verbindung aufgebaut werden. Eine detailliertere Beschreibung findet sich hier.

Auch wenn kein Sprite Sheet genutzt wird, weil bspw. stattdessen eine Icon Font verwendet wird, macht es häufig Sinn, ein Tool zur Optimierung und Komprimierung von Bildern sowie zum Erstellen von verschiedenen Bildgrößen für verschiedene Devices in die Toolchain zu integrieren.

Stylesheets

Auch beim Entwickeln von CSS möchte man ungern auf die Vorteile verzichten, die moderne Stylesheet-Sprachen bieten. Sie bieten u.a. die Möglichkeit, verschachtelte Selektoren, Variablen, Funktionen und wiederverwendbare Blöcke (sog. mixins) nutzen zu können. Da gängige Browser die Sprachen nicht interpretieren können, müssen sie von einem Präprozessor zu regulärem CSS kompiliert werden.
Außerdem können Build-Tools das generierte CSS mit vendor prefixes versehen und die Dateien minifizieren.

Javascript

Um die angesprochenen ES6-Features nutzen zu können, ohne ältere Browser auszuschließen, gibt es zwei Technologien, die die Funktionalität in alten Browsern nur in Kombination sicherstellen. Der ES6-Code kann durch einen Compiler bzw. Transpiler in ES5-Code übersetzt werden. Allerdings bietet ES6 Funktionalitäten, die nicht ohne weiteres in ES5 übersetzbar sind, bspw. Promises für asynchrone Funktionen. Dazu müssen sog. Polyfills geladen werden — Code-Bausteine, die diese Funktionalitäten nachrüsten.

Um beides nicht manuell handhaben zu müssen, kann man das Kompilieren und Anreichern mit Polyfills automatisieren, indem man einen Compiler wie Babel in den Build-Prozess aufnimmt. Babel ergänzt zudem bei Bedarf die benötigten Polyfills.

In vielen Umgebungen ist es erforderlich, von reinem Javascript zu abstrahieren, um bspw. Typsicherheit auch in Javascript nutzen zu können. Die populärste Programmiersprache hierzu ist TypeScript, eine Programmiersprache von Microsoft, die mit eigener Syntax daherkommt und zu reinem Javascript kompiliert wird. Der Compiler wird in der Regel in den Build-Prozess eingebaut, damit die Dateien nicht manuell kompiliert werden müssen.

Ähnlich verhält es sich bei Javascript Frameworks wie bspw. Vue. Insbesondere die Single File Components mit der Dateiendung .vue kann ein Browser nicht interpretieren. Auch hier wird ein Compiler im Frontend Build benötigt, der die Vue-Dateien in Javascript übersetzt.

Der Build-Prozess @ Chrono24

Bei Chrono24 stellt die große Software- und Serverlandschaft eine Herausforderung für Build- und Deployment-Prozesse dar. Daher wurde in der Vergangenheit ein sehr individuelles internes Toolkit entwickelt, um den Frontend Build zu automatisieren. Dieses Toolkit wurde stetig weiterentwickelt und ist so mit der Zeit immer weiter gewachsen.

Im Laufe der letzten Jahre haben wir, dank der Vielzahl der Open Source Tools, einen Großteil des Custom-Codes zurückgebaut und die Prozesse mit Hilfe von Standard-Tools abgebildet. Teilweise haben wir bestehende Libraries angepasst oder erweitert, um die Tools an uns anzupassen — nicht andersherum!

Bei unserem Frontend Build-Prozess setzen wir auf Node.js für Dateizugriffe, den mitgelieferten Node Package Manager zum Installieren und Verwalten von Bibliotheken, den Task-Runner Gulp zum automatisierten Ausführen von Build-Skripten sowie Webpack zur Verwaltung der erzeugten Files.

Bei uns lässt sich der Build in 3 Phasen unterteilen:

1. Transpilieren und Kompilieren

Im ersten Schritt werden unsere Vue-Komponenten, unsere TypeScript- und ES6-Module und auch unsere ES5-Skripte kompiliert bzw. transpiliert. Außerdem werden unsere Stylesheets aus den SCSS-Quelldateien erzeugt. Hierzu verwenden wir eine Kombination aus Gulp und Webpack. Während die Vue-Komponenten und TS- bzw. ES6-Module von Babel (in Form eines Webpack-Plugins) verarbeitet wird, setzen wir bei unseren Stylesheets und unserem Legacy Code in ES5-Skripten auf eigene Gulp-Skripte. Für beides können wir Webpack (noch) nicht verwenden, da unsere ES5-Skripte mit dem Google Closure Compiler kompiliert werden, für den es keine effiziente Webpack-Integration gibt, und Stylesheets nur über Umwege durch Webpack erzeugt werden können. Die Zahl der ES5-Skripte nimmt jedoch dank regelmäßigen Refactorings weiter ab und das Javascript kann in naher Zukunft komplett über Webpack-Plugins kompiliert werden.

2. Bundling

Um das Erzeugen der Javascript-Bundles für die einzelnen Bereiche unserer Plattform kümmert sich Webpack. Dabei werden alle Imports aufgelöst sowie externe Bibliotheken importiert. Das hat den Vorteil, dass wir am Ende nur ein Javascript-File einbinden müssen und unser Markup nicht mit überflüssigen script-tags aufblähen. Außerdem bietet Webpack die Möglichkeit, den Browser-Cache für geänderte Dateien zu invalidieren. Dazu werden Hashwerte über die Dateiinhalte generiert und an die Dateinamen gehängt. Sobald sich eine Datei ändert, ändert sich automatisch der Dateiname und der Browser muss die Datei frisch vom Server anfragen. Dadurch werden Änderungen sofort sichtbar und nicht erst dann, wenn der Browser-Cache geleert wird.

3. Minifizieren

Im letzten Schritt werden die generierten Dateien minifiziert und Source Maps für die generierten CSS- und Javascript-Dateien erzeugt. Source Maps sind kleine Dateien, die Informationen über die zugehörige minifizierte Datei beinhalten und mit deren Hilfe die ursprüngliche Datei wiederhergestellt werden kann. Moderne Browser können diese Source Maps interpretieren und damit eine enorme Hilfe beim Debugging sein. Sie werden nur aufgrund der Minifizierung benötigt.

Obwohl die erzeugten Dateien mittels gzip komprimiert werden, um Ladezeiten zu minimieren, kann auf die Minifizierung nicht verzichtet werden. Gzip ist eine verlustfreie Kompression, d.h. es werden keine Code-Optimierungen vorgenommen. Beim Minifizieren hingegen werden Variablen umbenannt und alle überflüssigen Zeichen wie Leerzeichen, Zeilenumbrüche oder Kommentare entfernt, um die Datei zu verkleinern und damit die Ladezeit zu reduzieren.

Das Beispiel an der beliebten Javascript-Bibliothek JQuery zeigt eindrucksvoll, wie stark die Kombination aus Minifizierung und gzip-Kompression die Dateigröße beeinflussen kann:

Fazit

Bei Chrono24 tun wir viel dafür, mit unseren Workflows und dem dazugehörigen Tooling am Zahn der Zeit zu bleiben. Auch im Bereich Frontend Build geht die Entwicklung ständig weiter und es etablieren sich in regelmäßigen Abständen neue Standards, die wir so weit wie möglich in unseren Workflow aufnehmen — wenn sie unsere Prozesse unterstützen und nicht behindern.

Bildquellen