Grunt – wir verbessern Code und Workflow im Frontend (Part 1)

By | 19. Dezember 2012

Grunt Logo

Hinweis: dieses Tutorial bezieht sich noch auf eine Gruntversion 0.3x! Für die aktuelle 0.4.x sind neue Anforderungen, die dieses Tutorial (noch) nicht abdeckt!

Ein Frontendentwickler hat heutzutage nicht mehr nur die Aufgabe ein paar seltsame HTML-Tags ins Frontend zu zaubern und dabei darauf zu achten, dass es doch irgendwie in das angestrebte Design passt. Viel mehr hat er sich auch mit rumzuschlagen, dass er parallel mehrere Tools bedienen muss um an sein Ziel zu kommen. Sei es die Verwendung von HTML5 mit Fallbacklösungen für ältere Browser, CSS mit Gridsystem (oder sogar objektorientierten Ansätzen), JavaScript mit der Auswahl eines (oder mehrerer Frameworks wie Prototype oder jQuery) oder die Optimierung von Bildern in Sprites und das entfernen der Metainformationen, dass es auch ja alles schön klein wird (oder generell: Minifizierung). Toll wäre doch hier ein standardisierter Workflow. Dabei kann Grunt helfen.

Grunt ist ein von Ben Alman entwickeltes Kommandozeilen Build Tool für die Frontendentwicklung. Wer einmal mit Maven / Ant gearbeitet, kennt die Vorteile eines solchen Build Tools.

Was ist ein Build Tool?

Dabei handelt es sich im Regelfall um ein Werkzeug für die Konsole (jetzt nicht weglaufen, es tut wirklich nicht weh 😉 !!), das bestimmte, immer wieder kehrende, Arbeitsabläufe abnimmt und automatisiert. Was typische Arbeitsabläufe sein können, habe ich oben schon angedeutet. Konkretisiert bzgl. Frontend wäre das z.B.: linten (Analyse eines Quelltexts auf potentielle Fehler bzw. gegen Konventionen), minifizieren (entfernen von unnötigen Zeichen aus Dateien), konkatenieren (verbinden von mehreren Dateien zu einer Datei) und testen (z.B. sogenanntes „test driven development„, bei dem es darum geht Programme gegen definierte Tests zu schreiben). Das sollte für den Anfang reichen an Aufgaben 🙂

Genau so ein Tool ist Grunt. Und das versuchen wir jetzt mal anzugehen …

Voraussetzungen für Grunt

Um Grunt nutzen zu können, sind ein paar wenige Voraussetzungen zu erfüllen:

  1. Installation von Node.js
  2. Installation von PhantomJS

Node.js

Dieses tolle (JavaScript) Framework dient dazu serverseitig Webapplikationen mit JavaScript zu erstellen. Dabei wird viel Wert auf Performance und Skalierbarkeit gelegt. Eine detailliertere Beschreibung findest du auf Wikipedia.

PhantomJS

Nur notwendig, wer sich auch mit Unit Tests beschäftigen will – also seinen JavaScript Code testen. Hierbei handelt es sich um einen sogenannten „headless WebKit“ mit JavaScript API. Es stellt sozusagen einen Browser für die Konsole dar (mit WebKit Engine). Wichtig: wer das Zip-Archiv unter Windows herunterlädt, sollte unbedingt die PATH-Variable entsprechend anpassen!!

Hinweis bevor es los geht

In diesem Tutorial werde ich kein eigenes PlugIn vorstellen, sondern das verwenden, was das Tool Grunt standardmäßig mitliefert. Alles andere würde den Rahmen hier sprengen.

Es steht dir selbstverständlich frei etwas eigenes zu benutzen 🙂

Wichtig: halte bis zum Ende durch – den das Beste kommt bekanntlich zum Schluss (Spoiler: das automatische ausführen von Prozessen, live während der Entwicklung … muhahah, wie geil)

Installation von Grunt

Ist (mindestens) Node.js installiert, lässt sich Grunt sehr bequem installieren. Dazu öffnest du einfach eine Konsole und tippst:

[code]npm install -g grunt[/code]

npm ist der Node Package Manager. Damit veranlässt du Node.js ein Paket zu installieren. Diesen Befehl solltest du dir merken, da wir damit später noch ein paar Mal in Kontakt kommen um zusätzliche PlugIns für Grunt zu verwenden. Der Schalter -g dient dazu das Paket Grunt global zu installieren. Damit ist es uns sozusagen systemweit verfügbar.

Der erste Kontakt – das erste Projekt

Um den Einstieg so bequem wie möglich zu gestalten, werden wir am Anfang erstmal ein leeres Projekt jQuery-Projekt erzeugen. Es ist später überhaupt kein Problem Grunt auch in vorhandene Projekte zu integrieren. Das wird allerdings einfacher fallen, wenn die Basics klar sind.

Hinweis: sämtliche folgende Befehle gehen von der Kommandozeile aus (im Wurzelverzeichnis des Projekts) und von einer Installation mit PhantomJS!

Hinweis für Windows User: anstatt des Befehls grunt kann es sein, dass du grunt.cmd ausführen musst – siehe Grunt FAQ.

Also starten wir mal durch. Navigiert in der Kommandozeile zu einem Ort eurer Wahl für das neue Projekt (z.B. in einem htdocs-Verzeichnis), erstellt einen Ordner „new_workflow“ (oder so etwas) und geht in diesen Ordner. Hier sollte natürlich nichts vorhanden sein 😉

Die Templates / Vorlagen & Initialisierung

Grunt bietet von Haus aus verschieden Vorlagen / Templates für die Erstellung eines Webprojekts. Diese Templates wiederum haben bestimmte Vorkonfigurationen. Welche standardmäßig installiert sind, erfahren wir, wenn wir folgenden Befehl eingeben:

[code]grunt init[/code]

Wichtig sind für uns erstmal die folgenden Vorlagen:

  • gruntfile – hier wird „nur“ die zentrale Konfigurationsdatei angelegt, keine Test- oder sonstigen Verzeichnisse
  • jquery – Vorkonfiguration für ein jQuery-Plugin mit Tests (QUnit) und Verzeichnisse für Drittanbieterbibliotheken usw.

Da wir wie gesagt mit einem jQuery Projekt, bzw. PlugIn, durchstarten wollen, erstellen wir uns also in unserem new_workflow-Verzeichnis ein neues Grunt-Projekt mit:

[code]grunt init:jquery[/code]

Die Fragerunde

Nach Bestätigung des init-Befehls von oben werden uns ein paar Fragen gestellt:

  • Project name“ – der Name des Projekt – Achtung: hier keine Leerzeichen verwenden; der Name wird später Dateiname unserer JavaScript-Datei; beachtet die Konvention, dass ein jQuery-PlugIn grundsätzlich so benannt werden sollte: jquery.pluginname.js – d.h. hier können wir z.B. angeben „jquery.newworkflow“ -> wird dann jquery.newworkflow.js
  • Project title“ – ein kurzer Title (gern auch wieder mit Leerzeichen), z.B. New Workflow
  • Description“ – eine Beschreibung des Plugins, z.B. demonstration for a new workflow with grunt
  • Version“ – initiale Version des PlugIns
  • Project git“ – (späteres) Git Repository
  • Project homepage“ – die Homepage auf der das PlugIn gestellt wird
  • Project issue tracker“ – z.B. für die Homepage für Bugzilla (vom PlugIn)
  • Licenses“ – unter welcher Lizenz das PlugIn gestellt werden soll (bitte MIT oder ähnliches :-P)
  • Author name“ – dein Name
  • Author email“ – deine E-Mail-Adresse
  • Author url“ – deine Website
  • Required jQuery version“ – welche jQuery-Version benötigt wird (mindestens)

Keine Angst, diese Fragerei findet nur im init-Task statt und die Antworten können später natürlich noch geändert werden. Meine Antworten sehen folgendermaßen aus:

Grunt Fragerunde

Die Ordnerstruktur

Nach der Bestätigung, dass keine weiteren Änderungen der Antworten vorgenommen werden müssen, haben wir jetzt ein paar Dateien und ein paar Ordner:

  • grunt.js – das sogenannte Gruntfile, die zentrale Konfigurationsdatei für das Projekt
  • Lizenzdateien – die typischen Sprüche
  • newworkflow.jquery.json – beinhaltet die Metainformationen zu dem Projekt, die aus den Fragen von oben generiert werden (und natürlich auch bearbeitet werden können
  • package.json – ein Relikt aus der Node.js-Welt, beinhaltet Informationen zur Entwicklung dieses Plugins (verwendete Node.js PlugIns usw.)
  • dist-Ordner: hier werden standardmäßig die Dateien abgelegt, die durch Grunt erzeugt werden (z.B. nach dem lint-Task usw)
  • libs-Ordner: hier kommen die Bibliotheken der Drittanbieter rein (z.B. jQuery, QUnit, Prototype usw.)
  • src-Ornder: beinhaltet unsere Quelldatei des Plugins (die wir gleich bearbeiten werden)
  • test-Ordner: ein Ordner mit den QUnit-Tests

Das Gruntfile grunt.js

Wie gesagt, ist das Gruntfile die zentrale Konfigurationsstelle für unser Projekt. Es wird bei jedem Durchlauf bzw. Start von Grunt ausgelesen und entsprechend der Einstellungen ausgeführt. Hier werden sämtliche Aufgaben und Optionen festgehalten. Meine, für dieses Tutorial erzeugte Datei, sieht folgendermaßen aus:

Bitte beachtet, dass ich sämtliche Teile für QUnit (Testing des Codes, was wir später sehen werden) auskommentiert habe.

Das Gruntfile ist grundsätzlich in drei Bereich untergliedert:

  1. Projektkonfigurationgrunt.initConfig( { … } ); – hier werden die verschiedenen Tasks (= Aufgaben, Prozesse) definiert und konfiguriert
  2. Laden von PlugIns / Task Foldersgrunt.loadNpmTasks( … ); – zusätzlich installierte PlugIns müssen nach der Installation mit npm manuell geladen werden (entweder über den Namen des PlugIns, oder es wird ein kompletter Ordner geladen)
  3. Tasks und Helpergrunt.registerTask( … ); – an dieser Stelle ist es möglich Tasks zu „registrieren“, d.h., dass es möglich ist diese Tasks über die Kommandozeile aufzurufen (dazu gleich mehr)

Hinweis: die Objekte pkg und meta werden weiter unten behandelt, da sie jetzt erstmal nicht soooooo wichtig sind.

Einmal starten bitte …

Bis jetzt war alles sehr theoretisch. Aber etwas Grundlagenwissen brauchen wir einfach 😛 Aber jetzt geht’s los. Ohne tiefere Kenntnisse gehen wir wieder in unser Projektverzeichnis auf der Konsole und starten Grunt mit:

[code]grunt[/code]

Ich hoffe du hast auch die entsprechenden QUnit-Stellen im Gruntfile auskommentiert. Dann solltet ihr folgende Ausgabe auf der Konsole sehen (Dateinamen usw. können sich natürlich von deinen Angaben unterscheiden):

Grunt - der erste Anlauf

Hast du dir das Gruntfile schon mal angeschaut, hast du eventuell in der letzten Zeile den Codeschnipsel …

[code]grunt.registerTask( ‚default‘, ‚lint concat min‘ );[/code]

… gesehen. Mit dieser Methode werden in Grunt sogenannte Tasks (Aufgaben) definiert. Der erste Parameter ist der Name der in der Kommandozeile dem Grunt-Befehl übergeben werden kann. Der zweite Parameter kann entweder ein String oder ein Array mit task-Befehlen sein. Das heißt in diesem Beispiel: wenn du in der Konsole grunt ohne weitere Angaben aufrufst, wird standardmäßig der default-Task angestoßen (der nicht im Gruntfile fehlen sollte). Damit sind folgende zwei Kommandozeilenbefehle gleich:

[code]grunt
grunt default[/code]

Beide Male wird der default-Task aufgerufen. Damit werden die Tasks lint, concat und min aufgerufen (2. Parameter). Jetzt haben wir eine fehlerfreie, fehlerfreie (Syntax) und konkatenierte Datei im dist-Ordner – yay 🙂

Wie die Tasks im Detail funktionieren, erfährst du jetzt …

Die (built-in) Tasks: Konfiguration und Eigenschaften

Wie oben schon beschrieben, wurden im default-Task drei Aufgaben angestoßen: lint, min und concat. Das sind Tasks, die bei der Installation von Grunt mitinstalliert wurden. Grundsätzlich muss jeder Task eingestellt werden (wird beim initialisieren bereits auf Standard gestellt). Der Aufbau ist grundsätzlich ähnlich (bei PlugIns muss die entsprechende Doku gelesen werden!!). Nehmen wir uns einmal den min-Task aus unserem Gruntfile als Beispiel:

  • min – das ist der Name des Tasks, der durch das entsprechende Paket (oder Plugin) vorgegeben wird, er kann nicht ohne Weiteres geändert werden und darf nur einmal vorhanden sein
  • dist – ist das standardmäßige „Target“ (Ziel), wird im Abschnitt „eigene Tasks definieren & die Targets“ erklärt, hier nur so viel: muss vorhanden sein und stellt eine Art Kontainer für den Task
  • src – steht vermutlich für „Source“ und kann wieder entweder ein String oder ein Array sein, mit Information darüber, welche Dateien – in dem Fall – minifiziert werden sollen. Die Syntax wird im nächsten Abschnitt „die Syntax im Gruntfile“ erklärt
  • dest – steht vermutlich für „Destination“ und stellt sozusagen die Einstellungen für die Ausgabe, der Wert dieses Objekts ist der Pfad (und eventuell Dateiname) für die minifizierte Datei

Das heißt: wird der Task aufgerufen, wird in das dist-Objekt geschaut, die in den darin aufgeführten src-Objekt hinterlegten Dateien als Eingabe genutzt, minifiziert und in den unter dest gelegten Wert (Pfad) abgelegt.

Dieser Aufbau ist sehr oft anzutreffen und relativ einfach zu verstehen. Auch viele Plugins nutzen diesen Aufbau (und zusätzliche Optionen).

Der Name des Tasks (z.B. hier min) wird dann im Gruntfile bei der Registrierung von Tasks verwendet – siehe default-Task am Ende der Datei.

Hinweis: die Optionen für JSHint, was verwendet wird um JavaScript auf potentielle Fehlerquellen zu untersuchen, kannst du der offiziellen Doku von JSHint entnehmen.

Die Syntax im Gruntfile

Diese Syntax kann auf den ersten Blick etwas verwirrend wirken. Ist es aber nicht. Eigentlich ist es ganz einfach – ganz ehrlich. Nehmen wir uns den zweiten Eintrag im src-Array (zum ersten Eintrag komme ich danach kurz):

[code]'<config:concat.dist.dest>'[/code]

Heißt: schau in die Projektkonfiguration (grunt.initConfig) in den Task concat – hier das dist-Target und hole die Datei(en), die als Ausgabe im dest-Objekt angegeben sind. Damit werden quasi die konkatenierten Dateien minifiziert 🙂

Selbstverständlich könnten an der Stelle auch hartkodierte Dateinamen angegeben werden, z.B.:

[code]’src/jquery.newworkflow.js'[/code]

Es könnten im src-Objekt auch kommasepariert Dateien angegeben werden:

[code]src:’src/foo.js‘, ’src/bar.js‘, ‚baz.js‘, [/code]

Prinzipiell können Dateiangaben auch über Platzhalter folgenden. Im folgenden Beispiel „nimm alle Dateien im Ordner src:

[code]src: ’src/*.js‘, [/code]

… oder auch „hole rekursive alle js-Dateien im src-Ordner (inkl. Unterverzeichnisse)“:

[code]src: ’src/**/*.js‘, [/code]

Eigene Tasks definifieren & die Targets

Selbstverständlich besteht die Möglichkeit neben dem default-Task eigene Aufgaben zu definieren. Damit erhält Grunt das erste Mal eine hohes Maß an Flexibilität (das zweite Mal sind u.a. die Targets).

Möchtest du beispielsweise einen Task anlegen, der eine bestimmte Datei nur minifiziert, kannst du folgendes Codeschnipselchen definieren:

Was passiert hier:

  • es wird der min-Task um ein Target erweitert, damit ergibt sich die Möglichkeit einem Task mehrere Subtasks zu hinterlegen, die in verschiedenen Situationen benutzt werden können
  • Registrierung eines tasks für den grunt CLI (command-line interpreter) hinzugefügt

Den neu angelegten Task für Grunt kannst du jetzt bequem in der Konsole aufrufen:

[code]grunt mymin[/code]

… und somit würde das entsprechende File minifiziert und in minifizierter Version im dist-Ordner gespeichert.

Wichtig: Targets werden immer in der Form task:target angegeben – also mit Doppelpunkt zwischen Task und Target. Nur so weiß Grunt, dass nicht das Standardtarget angepeilt werden soll!

das Beste: der watch-Task

Bisher haben wir gesehen, wie wir Grunt einsetzen können, welche Möglichkeiten Grunt bietet und wie man die Prozesse manuell anstößt – über die Kommandozeile mit dem grunt-Befehl.

Das – aus meiner Sicht – definitiv coolste Feature überhaupt ist aber der watch-Task 🙂

Es bietet dir die Möglichkeit live während des entwickeln Tasks auszuführen – okay, etwas präziser: nach der Speicherung der angegebenen Dateien in der watch-Konfiguration … BÄÄM!!! 🙂 Aber okay … wie sieht das genau aus. Schauen wir uns den Standard-watch-Task nochmal an:

… so schön 😛

Erstmal zur Erklärung was hier passiert: wird der watch-Task dem Grunt-Befehl übergeben, lauscht das Tool quasi an den angegeben Dateien. Änderst du jetzt eine Datei und speicherst sie ab, ruft Grunt automatisch die definierten Tasks auf. In unserem Beispiel also den lint-Task.

Die Angabe der Dateien machst du im files-Objekt (per default werden hier die Dateien aus dem lint-Task genommen). Zur Erinnerung: es kann ein String sein, oder ein Array mit Pfad- und Dateiangaben (oder die config-Syntax, klar).

Und die Tasks gibst du logischerweise im tasks-Objekt an – ähnlich der Angabe bei der Taskregistrierung. Eigentlich echt einfach, oder?

Gestartet wird das dann wieder über die Kommandozeile:

[code]grunt watch[/code]

Nochmal zur Erinnerung: die built-in Tasks, oder später auch die installierten PlugIns können dem grunt-Befehl ohne Probleme mitgegeben werden.

So … damit hast du erstmal den watch-Task gestartet. Jetzt heißt es ausprobieren. Ändere eine Datei die dem watch unterliegen und speichere sie ab – dann solltest du eigentlich etwas Bewegung in der Konsole sehen.

PlugIns

Oft hast du hier gelesen, dass man auch PlugIns installieren kann. Und tatsächlich … es geht wirklich 🙂

Eine Liste der vorhandenen Grunt-PlugIns findest du auf der Grunt Website – Übersicht der Grunt-PlugIns. Momentan gibt es etwa 310 PlugIns (Stand: 12/2012).

Installieren kann man die PlugIns wieder bequem über npm:

[code]npm install -g pluginname –save-dev[/code]

-g dient dazu das Plugin auf globaler (Node.js-) Ebene zu installieren. Lass diese Schalter weg und es wird im aktuellen Projektverzeichnis ein Ordner „node_modules“ angelegt. Den solltest du allerdings nicht z.B. mit auf Github hochladen, da das unnötigen Platz verbraucht.

Der Schalter –save-dev ist nicht notwendig, aber sinnvoll, da dadurch die package.json erweitert wird im Objekt „devDependencies“ (um das installierte PlugIn). Der Vorteil dabei: gebt ihr euer Projekt mal weiter (oder es wird auf Github gepusht), muss der nächste Entwickler, der das Projekt weiterführen möchte, nur in den Projektordner zu navigieren und

[code]npm install[/code]

ausführen. Jetzt wird die package.json-Datei ausgelesen und automatisch alle PlugIns installiert, die im Objekt devDependencies hinterlegt sind. Super, oder?

Interessante PlugIns wären zum Beispiel:

  • grunt-css – zum linten und minifizieren von CSS Dateien (prinzipiell kann man auch den built-in min-Task nutzen)
  • grunt-smushit – entfernt unnötige Metainformationen aus Bildern
  • grunt-html – validiert HTML-Dokumente
  • grunt-compass – Einbindung und Ausführung von Compass / SASS

Zusammenfassung

Ich wollte dir eine kleine Einführung in die Welt der effizienteren (hoffentlich) Frontendentwicklung mit Grunt geben. Die größte Vorteil liegt darin, dass typische Prozesse während des Entwicklungsprozesses automatisiert ausgeführt werden können. Ein weitere Vorteil ist die zentrale Konfigurationsdatei, die sämtliche Einstellungen für das Projekt enthält.

Ausblick

Im nächsten Abschnitt (Part II) gehe ich dann auf die Unit Tests mit QUnit ein. Damit kann auf saubere Art und Weise seinen Code testen und in die Welt des test driven development im Frontend eintauchen.

Was dafür allerdings zu beachten ist, wie man testbaren Code überhaupt schreibt und wie ein typisches jQuery-PlugIn entsprechend den Vorgaben aussehen könnte, sie du dann beim nächsten Mal 🙂

Für Kritik, Anregungen und solche Geschichten hinterlasst doch bitte einfach ein Kommentar.

So weit von mir … ich wünsche happy coding 🙂

Kommentar hinterlassen

4 thoughts on “Grunt – wir verbessern Code und Workflow im Frontend (Part 1)

  1. Moritz

    Tolles und einfach zu verstehendes Tutorial. Ich bin begeistert!=)
    Grunt ist wirklich eine tolle Sache. Die Automatisierung der verschiedenen Aufgaben nimmt bei einem Projekt erheblich viel Zeit ab. Anfangs merkt man das noch nicht so sehr, da man mit der Einrichtung und dem Experimentieren von Grunt beschäfftigt ist. Aber nach und nach macht es richtig Spaß sich wirklich voll und ganz auf das Programmieren konzentrieren zu können.

    Unit Tests und allgemein das Thema des Test Driven Designs finde ich sehr spannend und freue mich bald davon etwas zu lesen.

  2. Benno Post author

    Hallo Moritz,

    schön, dass dir der Beitrag gefallen hat und du Freude am TDD hast. Grund selbst ist tatsächlich super. Leider ist der Artikel schon etwas angegraut, aber einiges kann man auch mit Grunt 0.4+ noch benutzen. Interessant wird es dann, wenn du noch mit Code Coverage (z.B. blanket.js) arbeitest. Happy coding 🙂

  3. Jeanette

    I don’t even have ca0n;&#823beald I rarely watch TV. I typically just turn on Netflix if I’m bored. I can swing $8 a month, not so much in the double digits. It seems worthless to me.

  4. FirstAugustus

    I have noticed you don’t monetize your website, don’t waste your traffic, you
    can earn extra bucks every month because you’ve got hi quality content.
    If you want to know how to make extra $$$, search for: Boorfe’s
    tips best adsense alternative

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.