Wer sich mit der Entwicklung von Web-Applikation im MEAN-(MongoDb, Express, Angular, Node)-Stack befasst, wird schnell mit Hilfsprogrammen wie Grunt oder Gulp konfrontiert. Diese JavaScript-basierten Werkzeuge helfen dabei, den Build-Prozess zu automatisieren. Zwei einfache Beispiele, worum es geht:

  • Wenn Sie Paket mit Bower installieren, z.B. jQuery, landen allerlei Versionen im Projekt. Minimierte, Optimierte, Testcode, Rohcode usw. Daraus entsteht am Ende ein finales Paket, optimiert, minimiert und fertig zur Auslieferung an den Browser.
  • Werden Präcompiler benutzt, z.B. TypeScript für JavaScript oder LESS für CSS, so müssen diese vor der Auslieferung ausgeführt werden.

In rein JavaScript-basierten Umgebungen ist es nun sinnvoll, auch diese Aufgaben mit entsprechenden Werkzeugen zu erledigen. Zwei Werkzeuge haben sich dafür am Markt etabliert: Grunt und Gulp.

Die Frage ist also: Grunt oder Gulp oder beides?

Zuerst eine kurze Vorstellung der Werkzeuge.

Grunt

Grunt ist nach eigener Darstellung auf der Website ein „JavaScript based Task Runner“. Er dient der Automation von Entwicklungsvorgängen. Bei Grunt geht Konfiguration vor Programmierung. Ausgangspunkt ist das Gruntfile, mit dem die Aufgaben automatisiert werden. Grunt ist Plug-In-orientiert, d.h. es werden für viele typische Vorgänge fertige Module angeboten, die in das Gruntfile eingebunden werden.

Gulp

Gulp ist ein Workflow-basiertes System. Es setzt auf das Streaming-Modul von node auf. Abgewickelt werden auch hier Aufgaben. Die elegante Umsetzung verspricht weniger Konfiguration und mehr Flexibilität. Hier geht Programmierung vor Konfiguration. Auch für Gulp gibt es fertige Module, die Standardaufgaben erledigen.

Die Unterschiede

Konfiguration versus Programmierung

Grunt gibt einen klaren Weg vor – Konfiguration von fertigen Aufgaben.

Gulp ist sehr offen – primitive eingebauten Aufgaben, Mini-Aufgaben aus der Community und ein flexibler Weg, diese zu kombinieren.

Grunt lebt also vor allem von seinem Repository an Modulen. Diese sind via npm von Github beschaffbar und die Liste ist beeindruckend lang. Gulp ist jünger und hat weniger zu bieten, benötigt aber auch weniger, weil sich durch wenige Code-Elemente vieles in JavaScript erledigen lässt, wofür man in Grunt erst ein Plug-In bauen müsste.

Das Gruntfile ist JSON und ist sehr primitiv aufgebaut:

grunt.initConfig({
  clean: {
    src: ['build/app.js', 'build/vendor.js']
  },
  copy: {
    files: [{
       src: 'build/app.js',
       dest: 'build/dist/app.js'
    }]
  },
  concat: {
    'build/app.js': ['build/vendors.js', 'build/app.js']
  }
// ... other task configurations ...
});
grunt.registerTask('build', ['clean', 'bower', 'browserify', 'concat', 'copy']); 

Am Ende dieses Skripts organisiert der ‚build‘-Task die zuvor konfigurierten Elemente. Jeder Konfigurationsschritt ist unabhängig von anderen. Die Aufgaben werden sequenziell ausgeführt. Da in den meisten Fällen mit Dateien operiert wird, ist bei jeder Aufgabe eine Quelle und ein Ziel anzugeben. Grunt parst diese Angaben und führt die Kopiervorgänge dann aus.

Gulp ist dagegen reines JavaScript:

var gulp = require('gulp');
var sass = require('gulp-sass');
var minifyCss = require('gulp-minify-css');
var rename = require('gulp-rename');

//declare the task
gulp.task('sass', function(done) {
  gulp.src('./scss/ionic.app.scss')
    .pipe(sass())
    .pipe(gulp.dest('./www/css/'))
    .pipe(minifyCss({
      keepSpecialComments: 0
    }))
    .pipe(rename({ extname: '.min.css' }))
    .pipe(gulp.dest('./www/css/'))
    .on('end', done);
});

Hier werden die Dateioperationen zusammengefasst und dann über das Node-Modul Streams ausgeführt. Die Quellangabe wird an alle folgenden Module weitergereicht und dies ermöglicht sehr kompakte Angaben, auch wenn die Aufgaben komplexer werden. Im Hintergrund nutzt Gulp die Bibliothek Vinyl, eine Art virtuelles Dateisystem.

Arbeit mit dem Dateisystem

Beide Werkzeuge nutzen Node und dessen Module zum Arbeiten mit dem Dateisystem. Das ist bei Grunt weitgehend ‚fs‘, bei Gulp eher ’stream‘. Grunt schreibt viele Dateien während des Prozesses und ist strikt dateiorientiert. Streams sind dagegen meist im Speicher und deshalb erfolgen viele Prozesse bei Gulp „in memory“. Nun sind viele Projekte eher klein und der Vorteil bei der Geschwindigkeit kaum messbar. Wer ein Projekt nach Tagen der Arbeit für die Auslieferung fertig macht wird den Unterschied zwischen 400ms und 800ms kaum bemerken (In der Praxis kann das aber auch mal 25ms zu 1200ms sein).

Community

Grunt ist länger am Markt als Gulp und hat eine deutlich größere Community. Allerdings ist die Zunahme bei Gulp erheblich und ein Ausgleich absehbar. Je nach Stärke der nächsten Versionen könnte Grunt den Vorsprung halten oder Gulp könnte auch überholen. Grunt ist derzeit Version 0.4.6 (Stand 09/2015). Die Versionierung ist eher die typische, sehr zurückhaltend und vorsichtige Strategie wie bei vielen Unix-Projekten. Gulp ist dagegen sehr aggressiv und derzeit bereits bei 3.9.0 (seit 2013).

Dokumentation

Die Website und API-Dokumentation zeigt mehr Reife, Umfang und Qualität bei Grunt. Gulp ist eher rudimentär und Quellcode lesen ist an der Tagesordnung. Während es endlose Artikel und Blogs zu beiden gibt, ist eine zentrale Website gerade für Einsteiger ein wichtiger Punkt. Bei Gulp ist davon nicht viel zu sehen.

In Grunt ist schneller konfiguriert – wenige fertige Aufgaben und alles steht bereit. Gulp setzt statt dessen auf verkettete JavaScript-Callbacks. Die Verkettung macht den Code aber auch kompakt und direkt. Grunt-Plug-Ins erscheinen mehr auf ihre Aufgabe zugeschnitten und konkreter an das Buildsystem Grunt angepasst. Gulp ist dagegen clever programmiert und spürbar schneller sowie sehr viel flexibler bei komplexeren Aufgaben. Plug-Ins für Gulp sind eher Node-Stream-Module als explizite Gulp-Plug-Ins. Sie erscheinen weniger stark auf ihren Zweck zugeschnitten als bei Grunt.

Profientwickler greifen häufiger zu Gulp, weil es schneller und flexibler ist. Für Anfänger mag Grunt einfacher erscheinen und ist oft ausreichend und zudem besser dokumentiert.

Was also sollten Sie nun nehmen? Wenn Sie bereits ein Werkzeug im Einsatz haben und zufrieden sind, dann besteht kein Grund zu wechseln. Wenn Sie lange Jahre entwickeln, viel Erfahrung haben (PHP, ASP.NET, Ruby oder so) und nun Node erobern, versuchen Sie Gulp. Ist alles noch neu und Sie wollen überhaupt mal ein Erfolgserlebnis haben, greifen Sie zu Grunt.

Alternativen

Die Frage ist nun noch, ob es nur Grunt und Gulp gibt? Nein, natürlich nicht. Hier ein kleine Liste alternativer Build-Werkzeuge in JavaScript:

Daneben eignet sich auch npm – der Node Package Manager. Die Konfigurationsdatei einer Node-Applikation, package.json, enthält die Option, Skripte zu bestimmten Zeitpunkten auszuführen. Das betrifft auch den Build-Zeitpunkt. Tests lassen sich hier z.B. leicht auslösen (etwas, was Grunt und Gulp auch sehr gut können):

"devDependencies": {
    "jshint": "latest",
},
"scripts": {
    "lint": "jshint **.js"
}

Wenn Sie also Grunt oder Gulp einsetzen möchten, um Unit Tests zu starten, dann darf man schon die Frage stellen, warum dies nicht gleich mit npm erledigt werden kann. Allerdings kann npm mit komplexen Aufgaben auch überfordert sein. Wenn aber ein Plug-In für Grunt oder Gulp nicht bereit steht, weil das Paket aktualisiert wurde und niemand schnell genug die Anpassung vorgenommen hat, legt man ein paar Wartezyklen bei der Entwicklung ein. Bei Hobbyprojekten mag das akzeptabel sein, mit einem Kunden im Nacken eher nicht.

 

Gulp oder Grunt /Gr?u(nt|lp)/