Grunt: Der JavaScript-Task-Runner

Grunt für optimierte Frontend-Entwicklung

Rathes Sachchithananthan — 08.Jun.2015
in Web

Kennt ihr das? Ihr arbeitet an Frontend-Projekten und habt einen soliden Workflow, der vollständig automatisiert ist und ihr keine lästigen Aufgaben immer wieder macht?  Ja? Dann bringt euch der Artikel nicht weiter. Habt ihr gerade mit „Nein“ geantwortet, dann kann ich euch weiterhelfen. Ich zeige euch wie ihr mit Grunt und weiteren einfachen Tools euren Workflow optimieren könnt.

Was ist Grunt?

Grunt ist wie sie auf ihrer eigenen Webseite schreiben, ein Task-Runner. Man kann für immer wiederkehrende Aufgaben Skripte für Grunt schreiben und diese darüber ausführen. Man kann die Aufgaben noch weiter automatisieren und so den Workflow mit Grunt optimieren.

Mein Workflow ohne Grunt

Ich zeige euch im Folgenden anhand eines typischen, simplen Workflows wie ich Grunt einsetze. Man muss dazu sagen, dass ich bei der Frontend-Entwicklung viele Frameworks und Bibliotheken benutze. Angefangen mit Twitter Bootstrap für CSS bis hin zu ganz neuen Sachen wie ReactJS im JavaScript.

Am Anfang des Projektes lade ich alle benötigten Bibliotheken mithilfe von Bower. Bower ist im Prinzip das, was Composer für PHP ist. Ich schreibe HTML-Code und entwickle die Stylesheets mithilfe von LESS oder SASS. Außerdem wird viel eigener JavaScript-Code geschrieben. Viel JavaScript nicht jQuery. Nur mal so als Anmerkung am Rande: Ich mag jQuery nicht so sehr, wenn es im richtige Webapplikationen geht.

Die SCSS Dateien werden kompiliert, die JavaScript-Dateien alle zusammengefasst und beide werden noch minifiziert und werden dann in das Projekt eingebunden. Parallel zu der Entwicklung teste ich den Code, den ich schreibe, direkt im Browser.

Allein schon beim Lesen merkt man wie viele Schritte ich durchführen muss, wenn ich nur mal kurz etwas testen will, was ich zuvor geschrieben habe: Kompilieren, Zusammenfassen und minifizieren. Dann den Browser aktualisieren und das Resultat ansehen. Das sind Aufgaben, die immer wieder vorkommen und diese werden automatisiert. Und zwar mit Grunt.

Voraussetzungen, Installation und Einrichten von Grunt

Grunt setzt auf NodeJS. Deshalb ist die Voraussetzung für den Einsatz von Grunt eine aktuelle Version von NodeJS und natürlich dem Node Package Manager (npm). Wie man NodeJS für Windows installiert erfahrt ihr auf NodeCode.de. Habt ihr NodeJS installiert, dann könnt ihr mit folgendem Befehl auch Grunt direkt installieren.

npm install -g grunt-cli

Damit ist Grunt installiert und ihr könnt anfangen Grunt in euren Projekten zu benutzen. Geht dazu in das Root-Verzeichnis eures Projektes und führt folgenden Befehl aus.

npm init

Damit erzeugt ihr ein Projekt, welches mit npm läuft. Das verhält sich so ähnlich wie bei Git. Wenn ihr den Befehl ausführt, folgt einfach der Anleitung in der Console. Ist das Projekt eingerichtet, dann können Grunt und die Grunt-Plugins in das Projekt eingebunden werden.

npm install grunt --save-dev
npm install grunt-contrib-concat --save-dev
npm install grunt-contrib-less --save-dev
npm install grunt-contrib-uglify --save-dev
npm install grunt-contrib-watch --save-dev

Die erste Zeile installiert Grunt selbst. In den folgenden Zeilen werden die Grunt-Plugins für das Zusammenfügen von Dateien (concat), das Kompilieren von LESS (less), das Minifizieren von JavaScript-Dateien (uglify) und ein Watcher hinzugefügt (watch). Mit dem Zusatz –save-dev sichert ihr, dass die Installationen auch in das NodeJS-Projekt festgehalten werden.

Bower für Bootstrap, AngularJS & Co.

Um Frontend-Bibliotheken zu laden benutze ich wie gesagt Bower. Falls ihr das noch nicht benutzt, dann könnt ihr das mit folgendem Befehl nachholen:

npm install -g bower

Erzeugt nun wie auch beim node-Projekt hier eine Projekt-Datei mithilfe des Befehls

bower init

Dieser Befehl erzeugt eine JSON-Datei mit dem Namen bower.json als eure Bower-Projekt-Datei für das Projekt. Ladet euch dann eure Bibliotheken über bower. Im folgenden Beispiel ziehe ich mir bootstrap via Bower:

bower install bootstrap -S

Wie auch bei Grunt wird hier mit einem Zusatz (hier -S) sichergestellt, dass alle Bibliotheken in der Bower-Projekt-Datei festgehalten werden. Beachtet auch, dass jQuery, welches ja eine Bedingung für Bootstrap ist, auch direkt mit heruntergeladen wird. Alle mit Bower heruntergeladenen landen in dem Ordner bower_components und können ab sofort benutzt werden.

Einsatz von Tasks in Grunt

Nun haben wir Grunt installiert, Grunt eingerichtet und mithilfe von Bower auch alle Bibliotheken geladen, die wir haben wollen. Jetzt ist es an der Zeit, dass wir unsere Plugins so einsetzen, dass uns die Arbeit erleichtert wird. Beachtet, dass der folgende Code-Abschnitt an eure Arbeitsweise angepasst werden muss.

Erstellt eine Datei mit dem Namen Gruntfile.js und erzeugt folgendes Grundgerüst:

// Gruntfile.js
module.exports = function(grunt) {

    // Initializing the configuration object
    grunt.initConfig({
    });

    // Plugin loading
    grunt.loadNpmTasks('grunt-contrib-concat');
    grunt.loadNpmTasks('grunt-contrib-watch');
    grunt.loadNpmTasks('grunt-contrib-less');
    grunt.loadNpmTasks('grunt-contrib-uglify');

    // Task definition
    grunt.registerTask('default', ['watch']);

};

Damit ladet ihr alle Plugins und legt als Default-Task „watch“ fest. Nun müsst ihr eure Tasks natürlich konfigurieren. Dies geschicht in der initConfig-Funktion.

LESS kompilieren mit Grunt

Fangen wir mit LESS an: Ihr legt fest, welche *.less-Datei zu welcher CSS-Datei wird, das war es schon. Außerdem könnt ihr über die optionen festlegen, ob die Datei minifiziert werden soll. Das Ganze sieht dann so aus:

less: {
    development: {
        options: {
            // minify the result
            compress: true
        },
        files: {
            // compiling core.less into core.css
            './style.min.css': './assets/styles/styles.less'
        }
    }
}

Ich lege hier fest, dass meine styles.less Datei, die alle weiteren LESS-Komponenten importiert, zu der CSS-Datei style.min.css kompiliert werden soll und dabei soll die Datei auch direkt minifiziert werden. Damit wären die Einstellungen für LESS fertig und man könnte jetzt mit dem Aufruf grunt less schon anfangen LESS-Dateien zu kompilieren.

JavaScript mit Grunt zusammenfügen und minifizieren

JavaScript wird mit zwei Plugins bearbeitet. Einmal mit grunt-contrib-concat für das Zusammenfügen und einmal mit grunt-contrib-uglify für das Minifizieren. Beides muss also eingestellt werden. Die Einstellung für das Zusammenfügen sieht folgendermaßen aus:

concat: {
    options: {
        separator: ';'
    },
    core: {
        src: [
            './bower_components/jquery/dist/jquery.min.js',
            './bower_components/bootstrap/dist/js/bootstrap.min.js',
            './assets/js/main.js'
        ],
        dest: './js/script.js'
    }
}

Ich lege hier also fest, dass ich die Dateien jquery.min.js, bootstrap.min.js und meine selbstgeschriebene main.js (nur als Beispiel hier, normalerweise ist das mehr als eine Datei) zusammengefasst werden sollen und in einer Datei script.js abgelegt werden sollen. Hiermit wäre der nächste Task verfügbar: grunt concat core würde jetzt das Zusammenfügen der Dateien ausführen. Bedenkt, dass „core“ ein von mir ausgedachter Name ist. Man könnte da alles mögliche verwenden. Außerdem kann man natürlich auch weitere Dateigruppierungen hinzufügen. Webapps haben bei mir zum Beispiel immer eine core-frontend.js und core-backend.js Datei.

Im nächsten Schritt muss diese script.js noch minifiziert werden. Die Dateien jquery.min.js und bootstrp.min.js sind zwar schon minifiziert, aber meine selbstgeschriebene Datei main.js ist es nicht. Daher muss die zusammengefasste Datei auch noch einmal minifiziert werden. Das geht mit uglify:

uglify: {
    options: {
        // Use if you want the names of your functions and variables unchanged
        mangle: false
    },
    scripts: {
        files: {
            './public/js/scripts.min.js': './public/js/script.js'
        }
    }
}

Das Prinzip ist wie oben auch schon immer: Man legt fest, welche Datei minifiziert werden soll und wo diese minifizierte Version landen soll. Die Option mangle: false sorgt dafür, dass eure Funktionsnamen und Variablen nicht verändert werden.

Nun haben wir also alle Tasks. grunt less, grunt concat und grunt uglify stehen zu Verfügung. Aber noch bringt uns das nicht sonderlich weiter. Wir müssen noch immer drei Befehle in die Konsole tippen, um die ganzen Aktionen durchzuführen. Das geht aber noch simpler: Mit einem Watcher

Automatisieren mit einem Watcher

Am Anfang hatten wir beim Grunt-Grundgerüst festgelegt, dass unserer Basis-Task die Watcher sein wird. Mit diesem können wir Aufgaben erstellen, wo bestimmte Dateien beobachtet werden und auf Änderungen weitere Tasks anstoßen. Zum Beispiel kann so automatisch bei der Änderung einer LESS-Datei der less-Task ausgeführt werden. Bei Änderungen in den JavaScript-Dateien könnten dann automatisch „concat“ und „uglify“ angestoßen werden.

Und genau das richten wir in Folgenden ein:

watch: {
    core: {
        files: [
            // watched files
            './resources/assets/js/main.js',
        ],
        // tasks to run
        tasks: ['concat:core','uglify:core'],
    },
    less: {
        // watched files
        files: ['./assets/styles/*.less', './assets/styles/modules/*.less', './assets/styles/base/*.less' ],
        tasks: ['less'],
    },
}

Wenn wir jetzt einmal den Befehl grunt watch ausführen, dann beobachtet Grunt nun die definierten Dateien auf Änderungen und feuert dann die dementsprechenden Tasks. Und genau das haben wir auch als Default-Task festgelegt. Wenn wir also den Befehl grunt ausführen, dann wird direkt grunt watch gefeuert und das fängt dann mit dem Beobachten an.

Fazit zu Grunt

Wir können jetzt die Konsole minimieren und anfangen zu arbeiten und müssen diese Aufgaben also nicht mehr mühselig immer wieder per Hand erledigen. Für mich bedeutet das enorm viel Arbeit, die mir da abgenommen wird. Grunt sollte man aber nicht nur auf die von mir vorgestellten Tasks reduzieren. Es gibt noch vieles mehr, was man ausprobieren könnte, ich aber noch nicht habe.

Was interessant sein könnte ist das Plugin, wo mit man PHP Unit-Tests auch feuern konnte, oder ein Skript, dass der Brower neugeladen hat. Es gibt halt viele Möglichkeiten, um Grunt gescheit und hilfreich einzusetzen. Wie sieht es bei euch aus? Ist Grunt bei euch im Einsatz? Welche Plugins benutzt ihr denn so? Schreibt mir in den Kommentaren oder auf den sozialen Netzwerken.

Rathes Sachchithananthan

Rathes Sachchithananthan

Hi, ich bin Rathes. Gründer dieses Blogs. Darüber hinaus habe ich Aheenam gegründet, eine Agentur für digitale Lösungen. Dort konzipiere und entwickle ich die digitale Weiterentwicklung meiner Kunden. Ich brenne für das Thema „Tamilen und Tamil Eelam“ und bin ein Microsoft-Fanboy. Du findest mich auch auf diversen sozialen Netzwerken

Web und die Welt — Gott kann auch mal drin vorkommen

Ein Blog über das Web und die Welt. Wir schreiben über viele interessante Themen wie z.B. das Web, Tamil Eelam oder über Fotografie. Vielleicht ist auch was für dich dabei?