Dienstag, 15. März 2016

Wie ich Grunt nutze

Ich möchte jetzt hier nicht die grundlegende Definition von Grunt, welche jeder auf der Projektwebsite nachlesen kann, wiederholen. Grunt ist für mich ein schicker Taskrunner, mit dem ich meine Projekte sehr gut umsetzen kann. Ich möchte euch daher kurz, knapp und sachlich aufzeigen, wie ich Grunt nutze und warum ich dass so mache.

Als erstes möchte ich kurz meine Verzeichnisstruktur darstellen, damit die Begründungen für meine Vorgehensweise besser von euch nachvollzogen werden können. Ich habe für mein Projekt ein Projektverzeichnis, in welchem die Gruntfile.js, die Package.json, die Bower.json, eine .bowerrc, eine README.md und zwei Configurationsdateien für Compass erstelle. Des Weiteren habe ich (vorerst) nur ein Verzeichnis namens public in diesem Verzeichnis. Später kommt durch die Installation der Node-Packages ein Verzeichnis namens node_modules hinzu. Die Bower-Packages lasse ich erst einmal in ein Unterverzeichnis ( public/js/libs ) installieren. Dies ist in der .bowerrc festgeleg. Sonst liegen unter public die üblichen Verdächtigen ( siehe unten im Dateibaum ).



public 
|-assets 
| |-images     // Verzeichnis für Bilder 
| |-fonts      // Verzeichnis für Webfonts 
|-css 
| |-style.scss 
| |-partials   // Verzeichnis für SCSS-Module 
| |-global     // Verzeichnis für globale SCSS-Module 
| |-modules    // Verzeichnis für SCSS-Module der Website 
|-js 
| |-app.js     // Einstiegs-Punkt für Javascript 
| |-libs       // Verzeichnis für die Bower-Packages 
| |-index.html


Da ich in meinen Projekten mit Sass/Scss arbeite, möchte ich mit Compass beginnen. Ich hab dafür zwei Konfigurationsdateien. Die config_development.rb ist wie der Name schon sagt für den Grunt Development Task gedacht. Die config_production.rb ist dafür da, im Build-Task nicht nur den Sass-Code zu kompilieren. Dort ist das Ausgabe verzeichnis gleich auf den Build-Ordner ( ./build/css ) eingestellt, welcher auf der gleichen Ebene wie public liegt. Alles was durch den Build-Task im gleichnamigen Verzeichnis landet ist später fürs Deployment auf den Server gedacht. In diesen Ordner werden auch nur die für den Betrieb der Seite / App benötigten Daten kopiert. Alles was für die Entwicklung gedacht ist, oder noch nicht Optimiert wurde ( Bilder etc ), bleibt im public-Verzeichnis, um auf dem Live-Server keinen unnötigen Overhead zu erzeugen.


module.exports = function( grunt ) {
    grunt.initConfig({
    pkg: grunt.file.readJSON('package.json'),
        compass : {
            dev: {
                options: {
                    config : 'config_development.rb' 
                } 
            }

            build : { 
                options : { 
                    config : 'config_production.rb' 
                }
             }
        },

        /** 
        * Hier kommen die restlichen Tasks hin
        **/

Als nächstes füge ich alle fertigen JavaScript-Datein und CSS-Dateien zu jeweils einer einzigen app.js bzw. style.css zusammen. Dadurch habe ich später nur noch jeweils eine Datei, die ich in der index.html importieren muss.


concat: { 
    options: { 
        separator: ';' 
    }, 
    js: { 
        src: ['Pfade', 'zu', 'den', 'JavaScript', 'Dateien'], 
        dest: 'public/js/dist/app.js' 
    }, 
    css: { 
        src: [ 'Pfad', 'zu', 'den', 'CSS', 'Dateien' ],
        dest: 'build/css/style.css' 
    }
},

Sind die JavaScript-Dateien zusammengefügt, jagen wir den Uglifyer drüber. Dieser schrumpft die Inhalte der Datei zu runter, dass das Script zwar noch funtioniert, aber praktisch ein riesiger großer Klumpen Spaghetticode ist. Warum? Weil dadurch schlicht weg die Dateigröße reduziert wird und später weniger Daten vom Server zum Client müssen und dadurch wiederum die Website bzw. die WebApp perfomanter wird.


uglify: {
    build: { options: {  
        options: {
            sourceMap: true,
            beautify: false, 
            mangle: false, 
            compress: true, 
            preserveComments: false 
        }, 
        src: 'public/js/dist/app.js', 
        dest: 'public/js/dist/app.min.js' 
    }
},

Dieser Task sorgt dafür, dass aus der zusammengeführten und komprimierten JavaScript-Datei sämtliche console log und debug Befehle entfernt ( gestripped ) werden. Dadurch verliert die Datei erneut ein paar Kilobyte an Größe.


strip: { 
    build: {
        src: 'public/js/dist.min.js',
        dest : 'build/js/app.min.clean.js', 
        options: {
             nodes: [ 'console.log', 'debug' ]
        }
 } 
},

Damit in der index.html später keine Referenzierungs-Fehler auftreten, müssen die einzelnen CSS-Links und JavaScript-Tags aus dem DOM entfernt und durch jeweils einen neuen ersetzt werden, der auf die passende Datei im build-Verzeichnis liegt. Zusätzlich wird an der durch einen Marker gesetzten Stelle in der index.html ein aktueller Zeitstempel als console log Befehl über JavaScript ausgegeben.


processhtml: {
    options: { 
        process: true
        templateSettings: {}, 
        data: { timestamp: "<script type='text/javascript'>if ( window.console && window.console.log ) { console.log('##### Portfolio (release: " + Date.now() + ") #####') }</script>" 
        }
    },
    build: { 
         files: { 
             'build/index.html': ['public/index.html'] 
         } 
    } 
},

ImageOptim ist ein Tool für Mac-User, mit der Möglichkeit einer Integration in Grunt. Der Task sucht in dem angegebenen Verzeichnis nach Bild-Dateien und versucht diese für das Web zu optimieren. Dadurch kann die Dateigröße teilweise drastisch reduziert werden. Genauere Informationen zu diesem Tool gibt es auf der Hersteller-Website.



imageoptim: { jpegMini: true,
    options: {
        jpegMini: true,
        imageAlpha: false,
        quitAfter: true
    },
    build: { 
        src: 'public/assets/images'
    } 
},

Um alle kompilierten Daten für das Deployment separiert bereitstellen zu können werden diese nun zu der CSS-Datei in das Build-Verzeichnis kopiert.


copy: { 
    build: { 
        expand: true,
        cwd: 'public/assets/', 
        src: '**', dest: 'build/assets' 
    } 
},

Hier füge ich dem standard Grunt watch-Task den Befehl hinzu, Änderungen im CSS-Verzeichnis zu beobachten und darauf hin den compass development Task auszuführen.


watch: { 
    css: { 
        files: [ 'css/**/+.scss' ],
        tasks: ['compass:dev'] }
}


Zu guter Letzt werden noch die Grunt-Plugins geladen und die Aufgaben für einen Build-Task definiert. Dieser Task enthält alle oben aufgeführten Tasks in einer sinnvollen reihenfolge.


    });

    grunt.loadNpmTasks('grunt-contrib-requirejs'); 
    grunt.loadNpmTasks('grunt-contrib-compass'); 
    grunt.loadNpmTasks('grunt-contrib-uglify'); 
    grunt.loadNpmTasks('grunt-contrib-concat'); 
    grunt.loadNpmTasks('grunt-contrib-watch'); 
    grunt.loadNpmTasks('grunt-contrib-copy'); 
    grunt.loadNpmTasks('grunt-processhtml'); 
    grunt.loadNpmTasks('grunt-imageoptim'); 
    grunt.loadNpmTasks('grunt-strip'); 

    // Grunt Task(s). 
    grunt.registerTask('build', ['compass:compile', 'concat:js', 'concat:css', 'uglify:build', 'strip:build', 'processhtml:build', 'imageoptim:build', 'copy:build'] );

};
Löst man anschließend mit grunt build als Befehl im Terminal den Build aus, sieht man, wie die einzelnen Aufgaben nach und nach ausgeführt werden. Treten Fehler auf, werden diese auf dem Bildschirm ausgegeben und der Build-Prozess abgebrochen. Weiss man, dass der Fehler oder die Warnung zu vernachlässigen ist, kann man den Prozess mit dem Parameter --force dazu bringen diesen Fehler bzw. die Warnung zu ignorieren und trotzdem alle weiteren Aufgaben zu durchlaufen.

Keine Kommentare:

Kommentar veröffentlichen