Digital Know How

Für ihren Fortschritt

Dieses Tutorial beschäftigt sich mit einem wichtigen Schritt zur Veröffentlichung einer App oder Webseite mit AngularJS – dem Paketieren und Minifizieren der Quelldateien. Wie im Beitrag Tutorial – AngularJS mit RequireJS beschrieben, lassen sich auch AngularJS Anwendung leicht mit RequireJS strukturieren und optimieren. Es gibt es jedoch Fälle, wie zum Beispiel – mobile Apps – wo das Nachladen von Modulen nicht unbedingt notwendig ist. Trotzdem kann RequireJS benutzt werden, um z.B. eine geeignete Projektstruktur zu erhalten, externe Bibliotheken zu verwalten oder ein einheitliches Programmierschema für AngularJS Anwendungen zu etablieren (Teamarbeit).

Voraussetzung

Umsetzung

Aus dem AngularJS mit RequireJS Tutorial ergibt sich folgende Ausgangs APP-Struktur.

Root
- app
  - controller
  - service
  - app.js
  - routing.js
  - main.js
- lib
- resource
- index.html

Der Inhalt der main.js ist unsere RequireJS Konfiguration und unser AngularJS Einstiegspunkt, der das Modul startet. Der Inhalt sieht wie folgt aus.

require.config({
    baseUrl: 'app',
    paths: {
        'angular': '../lib/angularjs/angular.min',
        ...
    },
    shim: {
        'angular' : { 'exports' : 'angular' },
        ...
    },
    callback: function () {
        'use strict';
        require([
            'angular',
            'routing'
        ], function (angular) {
            // init app
            angular.bootstrap(document, ['app']);
        });
    }
});

In app.js wird unser Angular-Modul definiert und routing.js enthält die States und Routen.
app.js

define([
    'angular'
], function (angular) {
    'use strict';

    return angular.module('app', []);
});

routing.js

define([
    'app'
    'controller/meinController'
], function (app) {
    'use strict';

    return app.config([
        '$stateProvider',
        '$urlRouterProvider',
        function ($stateProvider, $urlRouterProvider) {

            // url routes/states
        }
    ]);
});

Damit die App funktioniert, wird RequireJS und die Konfiguration in der index.html geladen.

<html>
    <head>
        <script src="lib/requirejs/requirejs.min.js" data-main="app/main"></script>
    </head>
    <body>
        ...
    </body>
</html>

Damit RequireJS-Projekte minifiziert werden können, müssen ein paar Änderungen in der App-Struktur gemacht werden. Derzeit wird die AngularJS-App in der RequireJS-Konfiguration gestartet, was nach dem Paketieren nicht mehr funktionieren würde, da die callback-Funktion nicht ausgeführt werden wird. Aus diesem Grund zieht dieser Teil in eine neue Datei – boot.js – um.

require([
    'angular',
    'routing'
], function (angular) {
    'use strict';
    // init app
    angular.bootstrap(document, ['app']);
});

Als nächstes muss die neue boot.js im Kopf-Bereich der index.html nach RequireJS geladen werden.
Die main.js verändert sich nun auch ein wenig. Die Konfiguration wird nun auf eine globale Variable require geschrieben und in der index.html vor RequireJS eingebunden.

var require = {
    baseUrl: 'app',
    paths: {
        'angular': '../lib/angularjs/angular.min',
        ...
    },
    shim: {
        'angular' : { 'exports' : 'angular' },
        ...
    }
};
<html>
    <head>
        <script src="app/main.js"></script>
        <script src="lib/requirejs/requirejs.min.js"></script>
        <script src="app/boot.js"></script>
    </head>
    <body>
        ...
    </body>
</html>

Jetzt kann schon über die Kommandozeile und dem r.js Module losgelegt werden. Damit jedoch kein komplizierter Befehl eingegeben werden muss, bietet das Modul an, eine Konfigurationsdatei – in diesem Fall build.js – anzulegen.
Diese ist eine Javascript-Datei, liegt im Basis-Ordner des Projektes und enthält folgende Zeilen.

({
    name: '../lib/requirejs/almond', // Pfad zu Almond -> Dafurch muss RequireJS nicht im Paket geladen werden
    include: ['boot'], // boot.js einbinden - muss extra angegeben werden, da diese nicht in der require-Konfig enthalten ist
    out: 'build/app.js', // Pfad für die paketierte und minifizierte Version
    baseUrl: 'app/', // Basispfad unserer App
    mainConfigFile: 'app/main.js', // Pfad zur Konfigurationsdatei
    optimize: "uglify2", // Wie optimiert werden soll (minifiziert werden soll)
    insertRequire: ['boot'], // Zu Beginn die boot.js laden, um die App automatisch zu starten
    wrap: true // Inhalt der Datei in anonyme Funktion packen
})

Der letzte Schritt ist das Bauen der Datei.
Über die Kommandozeile in das Projektverzeichnis wechseln und folgenden Befehl ausführen.

node r.js -o build.js

Nun sollte ein neuer Ordner out existieren und die paketierte und minifizierte app.js enthalten. Testweise können nun alle script-Blöcke in der index.html entfernt und dafür die build/app.js eingebunden werden.

Erweitert: Automatisierung

Für einen anständigen “Bauprozess” gehört natürliche eine Automatisierung, um einen neuen Build zu erstellen. Dadurch muss die index.html nachträglich nicht angepasst, sondern ein Ordner mit allen nötigen Quellen erstellt werden. Da dies nur ein Zusatz zum eigentlichen Tutorial ist, wird nicht jedes Detail erklärt.

Für die Automatisierung bieten sich Build-Systeme wie GruntJS oder GulpJS an. In diesem Beispiel wird Grunt genutzt. Auch hier kann das Node.js-Module einfach über npm installiert werden.

npm install -g grunt-cli

Für Grunt gibt es diverse nützliche Plugins, die für diesen Zweck genutzt werden können:

Damit diese genutzt werden können, müssen sie nach dem Installieren in der Grunt-Konfigurationsdatei geladen und konfiguriert werden. Solch eine Datei könnte wie folgt aussehen.

module.exports = function(grunt) {
    'use strict';
    // Project configuration.
    grunt.initConfig({
        requirejs: {
            compile: { // r.js Konfiguration
                options: {
                    name: '../lib/requirejs/almond',
                    include: ['boot'],
                    out: 'build/app.js',
                    baseUrl: 'app/',
                    mainConfigFile: 'app/main.js',
                    optimize: "uglify2",
                    insertRequire: ['boot'],
                    wrap: true
                }
            }
        },

        replace: {
            buildIndex: {
                src: ['index.html'],
                dest: ['build/index.html'],
                replacements: [{ // main.js entfernen
                    from: '<script src="app/main.js"></script>',
                    to: ''
                }, { // requirejs entfernen
                    from: '<script src="lib/requirejs/requirejs.min.js"></script>',
                    to: ''
                }, { // boot.js entfernen
                    from: '<script src="app/boot.js"></script>',
                    to: ''
                }, { // build app.js einkommentieren
                    from: '<!--<script src="build/app.js" type="text/javascript"></script>-->',
                    to: '<script src="app.js" type="text/javascript"></script>'
                }]
            }
        },

        copy: {
            main: {
                files: [
                    {
                        expand: true,
                        src: ['resources/css/**', 'resources/icons/**', 'resources/fonts/**', 'resources/img/**', 'app/templates/**'], // alle nicht minifizierten, statischen Dateien ins Build-Verzeichnis kopieren
                        dest: 'build/'
                    }
                ]
            }
        },
    });
    // contrib-copy laden
    grunt.loadNpmTasks('grunt-contrib-copy');

    // contrib-requirejs laden
    grunt.loadNpmTasks('grunt-contrib-requirejs');

    // text-replace laden
    grunt.loadNpmTasks('grunt-text-replace');

    // Build tasks
    grunt.registerTask('build', ['copy', 'requirejs', 'replace:buildIndex']);
};

Damit das Bauen funktioniert, muss in der Original-Index Datei die Paket-Datei (build/app.js) vorsorglich eingebunden und auskommentiert werden.

Nun kann über eine Kommandozeile einfach “grunt build” ausgeführt werden und im Ordner “build” steht das neue Package bereit.

Fazit

RequireJS ist ein mächtiges Tool, wenn es um das Paketieren und Minifizieren von Mobile- und Web-Apps geht. Über das behandeln von Javascript-Dateien, können auch CSS-Dateien minifiziert und paketiert werden. Kommt dazu noch ein Task-Runner, wie Grunt zum Einsatz, kann schnell und einfach ein solides Build- und Deployment-System geschaffen werden.

Wir freuen uns über eine Bewertung, um ein Feedback zu erhalten:

Durchschnittlich 2.3 Sterne aus 3 Meinungen.