© Excellent backgrounds/Shutterstock.com
Inkrementelles Frontend-Build-System auf Node.js-Basis

gulp


Die Frontend-Entwicklung hat in den letzten zehn Jahren stark an Komplexität gewonnen. Während sie sich früher vor allem mit statischem HTML und CSS beschäftigte, hat ihr Endprodukt heute zunehmend den Charakter einer vollwertigen Anwendung. Ein Build-System hilft, den hierfür benötigten Code zu modularisieren und erlaubt damit die Trennung von Belangen und Verantwortlichkeiten. Das Build-System ermöglicht inkrementelles Testing und Effizienz bei der Erledigung von Routineaufgaben wie beispielsweise Minifikation oder Konkatenation. Dabei sollten alle Aufgaben reproduzierbar, vollautomatisch und plattformunabhängig laufen.

Video: Unternehmenskritische Anwendungen mit JavaScript

Die hauptsächliche Programmiersprache eines Front­end-Entwicklers ist JavaScript. Seit 2009 mit Node. js [1] eine serverseitige JavaScript-Laufzeitumgebung veröffentlicht wurde, ist es deshalb naheliegend, das Build-System in derselben Sprache zu schreiben. Dies nicht nur in Honorierung von Jeff Atwoods Diktum „Any application that can be written in JavaScript will eventually be written in JavaScript“ [2], sondern vielmehr aus der Notwendigkeit heraus, das Build-System selbst unterhalten und weiterentwickeln zu können. gulp ist heute eines der meistverbreiteten Build-Systeme auf Node. js-Basis. Gründe dafür sind eine hohe Performance, die einfache Verwendung sowie ein hochwertiges Plug-in-Ökosystem.

Merkmale von gulp

gulp ist streambasiert. Ein Task liest standardmäßig Dateien vom Dateisystem, streamt sie durch eine beliebige Anzahl Plug-ins und schreibt das Resultat zurück auf die Festplatte. Insbesondere bei mehrstufigen Tasks ist es bezüglich Performance ein idealer Ansatz, da wiederholte Lese-/Schreibvorgänge andernfalls schnell zum limitierenden Faktor werden. Zu berücksichtigen ist, dass der Inhalt einer Datei standardmäßig komplett gebuffert wird, um das Handling zu erleichtern. Sollte das in Ausnahmefällen nicht erwünscht sein, kann das initiale Buffering deaktiviert werden.

Alternative Build-Systeme wie Grunt [3] zeichnen sich unter anderem dadurch aus, dass eine Task nicht programmiert, sondern konfiguriert wird. Dadurch können Einstiegshürden gesenkt werden, bei komplexen Tasks stößt der Ansatz jedoch bald an seine Grenzen. gulp geht deshalb den entgegengesetzten Weg: Jede Task wird explizit in Form von Code definiert. Das führt zu etwas mehr Schreibarbeit, das Resultat ist jedoch verständlicher und damit leichter wartbar.

Für Plug-ins gelten strikte Regeln [4]: Sie dürfen nur eine einzige Aufgabe lösen (ein Plug-in zur Konkatenation soll beispielsweise nicht auch noch eine Minifikation durchführen), sie sollen keine Funktionalität duplizieren, die bereits mit existierenden Node.js-Modulen abgedeckt wird. Zudem müssen sie getestet werden. Was auf den ersten Blick naheliegend erscheint, ist keine Selbstverständlichkeit. Dadurch, dass diese Regeln seit Aufkommen von gulp konsequent durchgesetzt werden (unter anderem mit einer Blacklist von Plug-ins, die die eine oder andere Regel nicht erfüllen [5]), haben die veröffentlichten Plug-ins [6] meistens eine hohe Qualität.

gulp 4

Während der Entstehung dieses Artikels steht Version 4 von gulp bereits in den Startlöchern. Die nachfolgenden Codebeispiele beziehen sich allerdings auf gulp 3, da die mit dem Update verbundenen Änderungen noch nicht im Detail kommuniziert und dokumentiert wurden.

Ein Beispiel

Für die Beispiele wird eine Installation von Node.js mit npm als Package-Manager vorausgesetzt [7]. Wir empfehlen, gulp als globales npm-Paket zu installieren:

npm install -g gulp

Die im ersten Beispiel verwendeten gulp-Plug-ins können folgendermaßen installiert werden:

npm install gulp gulp-eslint gulp-sourcemaps gulp-concat gulp-uglify gulp-livereload connect serve-static

In der Praxis werden die Abhängigkeiten jeweils in einer Datei package.json definiert und dann implizit mit einem einfachen npm install installiert [8].

Aufbau einer Task

Die Task aus Listing 1 liest alle Dateien mit der Endung .js im Verzeichnis source/js/, lintet sie mit ESLint, konkateniert sie zu einer einziger Datei bundle.js, minifiziert diese mit UglifyJS (unter Generierung einer so genannten Source-Map, die das minifizierte Ergebnis zu Debugging-Zwecken mit dem Originalcode verknüpft), schreibt sie anschließend in das Verzeichnis build/js/ und informiert den Browser, dass sich die Zieldatei geändert hat. Die Task wird automatisch ausgeführt, sobald eine Quelldatei verändert wird. Das Resultat wird mittels lokalem Webserver auf Port 9000 ausgeliefert.

Listing 1

var gulp = require('gulp'), eslint = require('gulp-eslint'), sourcemaps = require('gulp-sourcemaps'), concat = require('gulp-concat'), uglify = require('gulp-uglify'), livereload = require('gulp-livereload'), connect = require('connect'), connectServeStatic = require('serve-static'), http = require('http'); // JavaScript-Dateien konkatenieren und minifizieren gulp.task('js', function() { return gulp.src('source/js/*.js') .pipe(eslint()) .pipe(eslint.format()) .pipe(sourcemaps.init()) .pipe(concat('bundle.js')) .pipe(uglify()) .pipe(sourcemaps.write(...

Neugierig geworden? Wir haben diese Angebote für dich:

Angebote für Gewinner-Teams

Wir bieten Lizenz-Lösungen für Teams jeder Größe: Finden Sie heraus, welche Lösung am besten zu Ihnen passt.

Das Library-Modell:
IP-Zugang

Das Company-Modell:
Domain-Zugang