grunt.js – Der automatische Alleskönner
Nagut, der Titel ist eventuell etwas übertreiben, allerdings lassen sich laut Grunt.js – Homepage viele Tätigkeiten automatisieren. Bisher hatte ich mit Grunt keinen Kontakt, möchte dies aber nun ändern. Ausschlaggebend hierfür war die Überprüfung einer meiner Webportale. Das Optimierungstool meinte, dass ich die JS- und CSS – Dateien zusammen fassen könnte, um dadurch Serverzugriffe zu reduzieren und somit auch die Ladezeiten zu verkürzen. Ich habe einen Weg gesucht, dies manuell zu machen. Dann dachte ich mir, wenn ich nun eine Änderung habe, dann muss ich diese Tätigkeiten immer wieder erneut machen, was auch nicht so brickelnd und außerdem auch noch fehleranfällig ist. Was aber noch schlimmer ist, man kann sich nach längerer Zeit oft nicht mehr erinnern, welche Befehle für die einzelnen Schritte verwendet wurden. Nach kurzer Recherche bin ich auf Grunt.js gestoßen.
Mein primäres Ziel mit Grunt ist es nun, mehrere JS- und CSS – Dateien automatisiert zusammen zu fassen und zu minimieren.
Grunt – The JavaScript Task Runner
Kurz zu Grunt.js. Den Task Runner gibt es derzeit in Version 0.4.5 (Stand 22.12.2015). Ziel des Projekts ist es, repetitive Tätigkeiten zu automatisieren. Dadurch werden Ressourcen und Zeit gespart und mögliche Fehler bei manueller Durchführung vermieden. Es gibt eine Vielzahl an Plugins, die viele unterschiedliche Tätigkeiten abdecken.
Grunt – Der schnelle Einstieg
Falls node.js nicht installiert ist, muss das nun nachgeholt werden. Ein Installationspaket für Windows und Mac stehen unter Node.js Installer bereit. Ihr braucht keine Angst zu haben, ich kenne mich mit node.js auch nicht gut aus.
Grunt selbst und optionale Plugins werden über npm, den Node Package Manager für Node.js installiert. Bevor Grunt installiert wird, sollte npm auf den aktuellen Stand gebracht werden.
1 | npm update -g npm |
Nachdem das Update erledigt ist, folgt die Installation von Grunt. Installiert wird Grunt über npm von einem globalen Archiv in dem die node Module gehostet werden. Mit der Option -g wird das Module global installiert und steht in jedem Projekt zur Verfügung.
1 | npm install -g grunt-cli |
Im Projekt-Ordner legen wir nun die Datei package.json mit folgendem Inhalt an:
1 2 3 4 | { "name": "gruntmynodedemo", "version": "0.1.0" } |
Diese Datei beinhaltet den Namen des Projekts und die Versionsnummer. Es können einige weiter Informationen gespeichert werden, dazu später mehr. Im Projektordner nun die Abhängigkeiten für Grunt einrichten. Für später werde dich auch gleich einige andere Pakete mit einrichten. Dazu nun im Projektordner folgendes eingeben:
1 2 3 4 5 6 7 8 | npm install --save-dev grunt npm install --save-dev grunt-sass npm install --save-dev grunt-contrib-concat npm install --save-dev grunt-contrib-cssmin npm install --save-dev grunt-contrib-jshint npm install --save-dev grunt-contrib-qunit npm install --save-dev grunt-contrib-uglify npm install --save-dev grunt-contrib-watch |
Für mein primäres Ziel, JS- und CSS – Dateien automatisiert zusammen zu fassen und zu minimieren, benötigen wir nicht alle oben installierten Module. Hierzu reichen vorab grunt-contrib-concat, grunt-contrib-uglify, grunt-contrib-cssmin und grunt-contrib-watch.
Wenn Ihr diese Schritte nun richtig gemacht habt, sollte eure package.json – Datei ähnlich der folgenden aussehen:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | { "name": "gruntmynodedemo", "version": "0.1.0", "devDependencies": { "grunt": "^0.4.5", "grunt-contrib-concat": "^0.5.1", "grunt-contrib-cssmin": "^0.14.0", "grunt-contrib-jshint": "^0.11.3", "grunt-contrib-qunit": "^0.7.0", "grunt-contrib-uglify": "^0.11.0", "grunt-contrib-watch": "^0.6.1", "grunt-sass": "^1.1.0" } } |
Die von mir gewählte Projektstruktur sieht nun wie folgt aus:
1 2 3 4 5 6 7 8 | |- dev |—- js |—- css | |- dist |—- js |—- css |- node_modules |
Der Ordner node_modules wurde automatisch erstellt und beinhaltet die installieren Pakete und deren Abhängigkeiten. Der Entwicklungsordner dev beinhaltet die Entwicklungsdateien. Mit Grunt sollen die Dateien nun automatisiert bearbeitet werden, und in den Distributionordner dist exportiert werden.
Damit das funktioniert, benötigen wir folgende gruntfile.js – Datei im Stammverzeichnis unseres Projektes:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 | module.exports = function(grunt) { grunt.initConfig({ pkg: function() { var prop = grunt.file.readJSON("package.json"); prop.build = { jahr: grunt.template.today("yyyy"), datum: grunt.template.today("dd.mm.yyyy"), uhrzeit: grunt.template.today("HH:MM:ss") }; return prop; }(), concat: { options: { separator: ";" }, dist: { files: [ { src: [ "dev/js/aek.js", "dev/js/admin.js" ], dest: "dist/js/output1.js" }, { src: [ "dev/js/aek.js", "dev/js/admin.js", "dev/js/site.js" ], dest: "dist/js/output2.js" }, { src: [ "dev/js/*.js" ], dest: "dist/js/output3.js" } ] } }, uglify: { options: { sourceMap: true }, dist: { options: { sourceMap: true, beautify: false, compress: true, mangle: false }, files: [ { src: [ "dist/js/output1.js" ], dest: "dist/js/output1.min.js" }, { src: [ "dist/js/output2.js" ], dest: "dist/js/output2.min.js" }, { src: [ "dist/js/output1.js", "dist/js/output2.js" ], dest: "dist/js/output1und2.min.js" }, { src: [ "dist/js/output3.js" ], dest: "dist/js/output3.min.js" } ] }, beautify_gruntfile: { options: { sourceMap: false, banner: "/*! Erstellt am <%= pkg.build.datum %> um <%= pkg.build.uhrzeit %> */\n", beautify: true, compress: false, mangle: false }, files: [ { src: [ "gruntfile.js" ], dest: "dist/js/gruntfile.min.js" } ] } }, cssmin: { options: { }, dist: { files: { "dist/css/style.min.css": [ "dev/css/font-awesome.min.css", "dev/css/jquery-ui.css" ], "dist/css/style2.min.css": [ "dev/css/*.css" ] } } }, watch: { dist: { files: [ "dev/js//**/*.js", "dev/css//**/*.css" ], tasks: [ "concat", "uglify", "cssmin" ] } } }); grunt.loadNpmTasks("grunt-contrib-concat"); grunt.loadNpmTasks("grunt-contrib-uglify"); grunt.loadNpmTasks("grunt-contrib-cssmin"); grunt.loadNpmTasks("grunt-contrib-watch"); grunt.registerTask("default", [ "concat", "uglify", "cssmin" ]); }; |
Diese Datei müsst ihr auf eure Dateien bzw. eure Ordnerstruktur anpassen. In meinem Beispiel habe ich Dateinamen teilweise direkt angegeben, sowie auch Platzhalter verwendet. Das hängt von euren Gegebenheiten ab.
Im Projekt – Ordner nun grunt – Eingeben, womit der registrierte Task default ausgeführt wird. Dieser beinhaltet nun die 3 anderen Tasks concat, uglify und cssmin.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | O:\Projektordner\gruntmynodedemo>grunt Running "concat:dist" (concat) task File dist/js/output1.js created. File dist/js/output2.js created. File dist/js/output3.js created. Running "uglify:dist" (uglify) task >> 4 sourcemaps created. >> 4 files created. Running "uglify:beautify_gruntfile" (uglify) task >> 1 file created. Running "cssmin:dist" (cssmin) task >> 2 files created. 115.46 kB → 95.89 kB Done, without errors. |
Nun solltet Ihr im Distributionordner dist die gewünschten Dateien haben.
1 2 3 4 5 6 7 8 9 10 11 | //führt den Task default aus, der mit grunt.registerTask definiert wurd grunt //führt den Task concat aus grunt concat //führt den Task uglify aus grunt uglify //führt den Task cssmin aus grunt cssmin |
Nachdem es etwas aufwendig ist, nach jeder Änderung den grunt – Befehl aufzurufen, gibt es auch hierzu eine automatische Variante.
Watch – Änderungen automatisch überwachen
Mit der Installation von grunt-contrib-watch wurde ein Dienst eingerichtet, der Änderungen an Dateien überwacht, und automatisch Tasks ausführen kann. Definiert wurden die Tasks im Abschnitt watch in der gruntfile.js – Datei.
1 2 3 4 5 6 | watch: { dist: { files: [ "dev/js//**/*.js", "dev/css//**/*.css" ], tasks: [ "concat", "uglify", "cssmin" ] } } |
Sobald es eine Änderung in einer JS- bzw. einer CSS-Datei gibt, werden die Tasks „concat“, „uglify“ und „cssmin“ ausgeführt. Gestartet wird die Überwachtung mit:
1 | grunt watch |
Damit ist mein primäres Ziel nun erreicht. Ich hoffe euch damit eine Hilfe gegeben zu haben, um lästige Tätigkeiten nicht mehr manuell machen zu müssen.