HTML5-Tutorium: JavaScript: Entwicklungsumgebung: Node.js: Unterschied zwischen den Versionen

aus GlossarWiki, der Glossar-Datenbank der Fachhochschule Augsburg
Keine Bearbeitungszusammenfassung
Keine Bearbeitungszusammenfassung
Zeile 69: Zeile 69:
npm install --save-dev webpack webpack-dev-server css-loader node-sass sass-loader extract-text-webpack-plugin
npm install --save-dev webpack webpack-dev-server css-loader node-sass sass-loader extract-text-webpack-plugin
--><!--grunt-cli jsdoc yuglify mincss coffee-script jshint -->
--><!--grunt-cli jsdoc yuglify mincss coffee-script jshint -->
Sie können das Projekt in WebStorm öffenen, um die zugehörigen Dateien mit dem
WebStorm-Editor bearbeiten zu können:
* „<code>File</code>“ → „<code>Open</code>“
* Zum zuvor angelegten Testordner navigieren und diesen mit „<code>OK</code>“ öffnen. Die Frage, ob Sie das Projekt im selben oder in einem neuen Fenster öffnen wollen, können Sie nach Belieben beantworten.
<source lang="bash">
Sie können Ihre JavaScript-Projekt-Dateien auch mit jedem beliebigen anderen Code-Editor (Atom, Sublime, Nodepad++, Emacs etc. pp.) bearbeiten. Das liegt ganz bei Ihnen. WebStorm ist allerdings sehr angenehm zu bedienen und enthält einen kleinen Web-Server, der die Entwicklung von JAvaScript-Code erleichtert. Außerdem enthält er ein Terminalfenster (Menü am unternen Fensterrand), der unter Windows allerdings
nur Windows-Befehle ausführen kann. Unter Windows verwende ich daher meist die (Git-)Bash.


Sie sollten sich angewöhnen, in der Datei <code>package.json</code> sofort nachdem Sie sie
Sie sollten sich angewöhnen, in der Datei <code>package.json</code> sofort nachdem Sie sie

Version vom 3. Oktober 2017, 13:23 Uhr

Dieser Artikel erfüllt die GlossarWiki-Qualitätsanforderungen nur teilweise:

Korrektheit: 3
(zu größeren Teilen überprüft)
Umfang: 2
(wichtige Fakten fehlen)
Quellenangaben: 2
(wichtige Quellen fehlen)
Quellenarten: 3
(gut)
Konformität: 5
(ausgezeichnet)

Vorlesung WebProg

Inhalt | Visual Studio Code | Node.js

Zur Unterstützung der Entwicklung von Web-Anwendungen kann Node.js eingesetzt werden: automatische Übersetzung von EcmaScript 6 in EcmaScript 5.1, automatische Generierung der Dokumentation, automatisches Testen der Anwendung etc.

Das heißt, auch wenn man keine Node.js-Server-Anwendung erstellt, ist Node.js ein sehr wertvolles und universell einsetzbares Werkzeug.

Installation

Unix-Shell

Web-Entwickler verwenden unter Windows i. Allg. ein Terminal, das eine Unix-Shell simuliert. Unter Linux und Mac OS X ist es nicht notwendig, eine separate Unix-Shell zu installieren, da diese dort sowieso schon existiert (Unix: bash, Mac OS X: terminal).

Installieren Sie „Git for Windows“ (https://git-for-windows.github.io/; wählen Sie bei jeder Frage die Default-Werte, außer Sie haben einen triftigen Grund, davon abzuweichen). In diesem Paket ist die „Git BASH“ enthalten. Das ist eine Terminal-Anwendung, die eine Linux-Bash emuliert und zahlreiche nützliche Befehle wie „which“, „vi“ ( :-) ) etc. zur Verfügung stellt.

Unter WebStorm steht ebenfalls ein Terminal zur Verfügung, in dem Sie Kommandozeilen-Befehle absetzen können:

  • Klick auf Icon in der linken unteren Ecke → Klick auf Terminal

Wenn Sie das Web-Storm-Terminal verwenden, befinden Sie sich automatisch im Wurzelverzeichnis des zugehörigen Projektes. Bei Im Allgemeinen müssen Sie jedoch mit Hilfe des Unix-Befehls cd <PFAD> in das Wurzelverzeichnis des Projektes wechseln, wenn Sie Befehle ansetzen wollen, die sich auf ein bestimmtes Projekt beziehen.

Node.js

Installieren Sie nun Node.js: https://nodejs.org/en/, https://nodejs.org/dist/latest/, Version 8.x. Achten Sie darauf, dass die Option „Add to PATH“ ausgewählt wurde.

Starten Sie ein Unix-Terminal (z. B. Mac-OS-X-Terminal, bash oder auch WebStorm: Klick auf Icon in der linken unteren Ecke → Klick auf Terminal). Öffnen Sie das Terminal, um zu testen, ob node funktioniert:

  • Geben Sie Folgendes in die Konsole ein: node -v

Wenn Node.js korrekt installiert und der Pfad mit den node-Binaries in die Systemvariable PATH eingetragen wurde, sollte die Versionsnummer von node ausgegeben werden.

Sie werden feststellen, das Node.js zum Großteil über Terminal-Befehle gesteuert wird. Zu Beginn mag das für eingefleischte Klickibunti-Fans ungewohnt sein, aber Sie werden sich schnell daran gewöhnen.

Wichtige Node.js-Pakete

Mit Hilfe von npm (npm = node package manager, https://www.npmjs.com/) können beliebige Node.js-Pakete installiert werden. Viele dieser Pakte könnte man global auf dem Rechner installieren. Die globale Installation hätte zur Folge, dass jedes Projekt auf diese Pakete zugreifen kann, Pakete also nicht mehrfach installiert werden müssen.

Im Allgemeinen ist es alledings besser, die Pakete für jedes Projekt separat zu installieren. Insbesondere für den Webpack-Dev-Server ist dies zwingend notwendig. Dazu muss man zunächst einen leeren Ordner anlegen, indem das neue Projekt liegt. Es kann ein beliebiger Ordner gewählt werden. Im Folgenden wählen wir /C/mmprog/test als Beispielsordner. (Bachaten Sie, dass in der GIT Bash unter Windows Linux-Pfad-Bezeichner anstelle von Window-Pfad-Bezeichnern verwendet werden.)

Öffnen Sie nun ihr bevorzugtes Unix-Terminal und geben Sie folgenden Befehle ein:

mkdir -p /C/mmprog/test  # Erzeugen Sie ein neues (leeres!) Projektverzeichnis.
                         # Bitte passen Sie diesen Pfad an Ihre Gegebenheiten an.
                         # Achten Sie darauf, dass es diesen Pfad vorher nicht gegeben hat.
cd /C/mmprog/test        # Hier müssen Sie den von Ihnen zuvor definierten Pfad angeben.  
npm init -y              # Erzeugen Sie ein neues Node.js-Projekt unter Kontrolle von npm.
npm install --save-dev grunt-cli babel-cli yuglify mincss jshint jsdoc ink-docstrap
ls -al                   # Sehen Sie sich an, welche Dateien und Ordner erzeugt wurden.
less package.json        # Sehen Sie sich den Inhalt der Konfigurationsdatei an.

Sie können das Projekt in WebStorm öffenen, um die zugehörigen Dateien mit dem WebStorm-Editor bearbeiten zu können:

  • File“ → „Open
  • Zum zuvor angelegten Testordner navigieren und diesen mit „OK“ öffnen. Die Frage, ob Sie das Projekt im selben oder in einem neuen Fenster öffnen wollen, können Sie nach Belieben beantworten.
Sie können Ihre JavaScript-Projekt-Dateien auch mit jedem beliebigen anderen Code-Editor (Atom, Sublime, Nodepad++, Emacs etc. pp.) bearbeiten. Das liegt ganz bei Ihnen. WebStorm ist allerdings sehr angenehm zu bedienen und enthält einen kleinen Web-Server, der die Entwicklung von JAvaScript-Code erleichtert. Außerdem enthält er ein Terminalfenster (Menü am unternen Fensterrand), der unter Windows allerdings
nur Windows-Befehle ausführen kann. Unter Windows verwende ich daher meist die (Git-)Bash.

Sie sollten sich angewöhnen, in der Datei <code>package.json</code> sofort nachdem Sie sie
erstellt haben, ein paar projektspezifische Informationen zu ergänzen, wobei Sie die Werte
natürlich an Ihre Gegebenheiten anpassen müssen:

<source lang="json">
  "kewords": ["test"],
  "author": "Wolfgang Kowarschick",
  "license": "CC-BY-NC-SA-4.0",
  "repository": {
    "type": "svn",
    "url": "https://glossar.hs-augsburg.de/kowa/tutorium/es5/test"
  },

Als Repository verwenden wir in der Vorlesung Subversion (svn). Üblicherweise kommt heute allerdings Git zum Einsatz. Ihr Repository-Pfad lautet im Rahmen der Vorlesung

   "https://praktikum.multimedia.hs-augsburg.de/svn/RZ-ACCOUNT/mmprog/PROJEKTNAME"

Ändern Sie dagengen nicht die Paketinformationen, die im Attribut devDependencies gespeichert sind. Diese werden mittels npm verwaltet. Der Node-Paket-Manager „npm“ ist ein sehr nützliches Hilfsprogramm, mit dem Sie neue Node-Projekte erstellen (npm init -y), Pakete installieren (npm install) und wieder löschen (npm uninstall) und vieles mehr machen können (Dokumentation: https://docs.npmjs.com/).

Wichtig ist auch der Befehl

npm update

Mit diesem können Sie die lokal gespeicherte Versionen der im Attribut „devDependencies“ aufgeführte Node.js-Pakete jederzeit auf die neueste Version aktualisieren. Die aktuell installierten Versionen können Sie mit

npm list

erfragen.

Sie können Pakete auch lokal löschen und dann mittels npm install wieder herstellen. Versuchen Sie es (Achtung: Die Unix-Befehle „ls“ und „rm“ funktionieren unter Windows nur in der Bash. In einem Windows-Terminal heißen die entsprechenden Befehle „dir“ und „del“ ):

ls -al node_modules # Liste alle Dateien und Verzeichnisse auf,
                    # die im Ordner node_modules enthalten sind.
rm -rf node_modules # Lösche diesen ORdner samt Inhalt.
npm list
ls -al node_modules
npm install
ls -al node_modules
npm list

Sie können auch überprüfen, ob Sie in Ihrem Projekt irgendwelche veralteten Pakete benutzen (https://docs.npmjs.com/cli/outdated):

npm outdated

Kein Ergebnis ist hier ein gutes Ergebnis!

Node.js-Pakete könnten mittels der npm-Option -g auch global gespeichert werden. Allerdings ist es besser sie mittels der Option --save-dev projektlokal zu speicher. Die kostet zwar mehr Platz, hat aber den Vorteil, dass man für verschiedene Projekte verschiedene Pakete verwenden kann. Es kann durchaus sein, dass zwei verschiedene Pakete ein drittes benötigen, doch jeweils in einer anderen Version. Außerdem stehen einem anderen Entwickler, der eine Kopie Ihres Projektes erhält, dann sofort alle Pakete in der jeweils benötigten Version zur Verfügung. Andererseits bläht sich dadurch ein Projekt sehr stark auf. Es enthält dann meist mehrere Hundert oder gar mehrere Tausend Dateien, obwohl das eigentlich Projekt nur aus ein paar wenigen Dateien besteht. Dies ist vor allem beim Speichern eines derartigen Projektes in eine Repository (SVN, Git etc.) sehr störend.

Um diese Probleme zu vermeiden, muss man die Speicherung von Node.js-Bibliotheken im Repository ausschließen und diese Dateien bei Bedarf auftomatisch von den entsprechenden Servern herunterladen. Wie das geht, haben wir gerade gesehen: Man läd Projekt auf seinen Arbeitsrechner, wechselt mittels cd ins Wurzelverzeichnis des Projektes und führt npm install aus.

Als nächstes initialisieren Sie Grunt (http://gruntjs.com/) . Grunt ist ein “JavaScript Task Runner”, also ein Programm, das Aufgaben (tasks) wie z. B. das Übersetzen von EcmaScript 6 in EcmaScript 5 oder das Komprimieren von Programmdateien automatisch durchführt.

Die Initialisierung erfolgt durch die Bereitstellung von zwei Dateien „package.json“ und „gruntfile.json“ im Root-Verzeichnis der Web-Anwendung. Die Datei „package.json“ haben Sie schon mittels npm init erstellt.

In der Datei „package.json“ gibt es das Attribut „main“. Es enthält den Namen der Konfigurationsdatei von Grunt: gruntfile.js. Diese fehlt allerdings noch. Also legen Sie sie im Wurzelverzeichnis des Projektes mit einem Texteditor Ihrer Wahl an:

gruntfile.js

module.exports = function(grunt)
{
  // Project configuration.
  grunt.initConfig
  ({
    pkg: grunt.file.readJSON('package.json'),
  });
};

Diese Datei ist noch ziemlich leer, aber das wird sich ändern.

Vorausschauend sollten Sie eine überflüssige WebStorm-Fehlerprüfung deaktivieren, da anderenfalls künftige Versionen Versionen der Datei „gruntfile.js“ jedes Mal eine Fehlermeldung zur Folge haben, wenn die Datei ins SVN-Repository geschrieben werden soll:

  • File bzw. WebStormSettings bzw. PreferencesEditorInspections
  • JavaScript aufklappen → General aufklappen → Unsued JavaScript / ActionScript global symbol deselektieren
  • OK

TO BE DONE

Ausschließen diverser Dateien von automatischer Speicherung und/oder Überprüfung

Sie sollten unbedingt verhindern das Node.js-Modulateien ins Repository geschrieben werden. Da ist, wie Sie zuvor gesehen haben, überflüssig und nimmt sehr viel Zeit in Anspruch, da es sich um Hunderte von Dateien handelt:

  • Rechtsklick im Dateibaum auf node_modulesSubversionIgnorenode_modules
  • Sollte der vorangegangene Schritt schief gegangen sein (WebStorms Subversion-Tools sind in dieser Hinsicht ziemlich schlecht), müssen Sie den letzten Schritt gegebenenfalls mit einem anderen Tool bewerkstelligen. Unter Windows ist Tortoise dafür recht gut geeignet.

Außerdem sollten Sie die automatische Überprüfung auf Fehler für alle Ordner ausschalten, die nicht von Ihnen stammen:

  • Rechtsklick im Dateibaum auf „node_modules“ → Mark Directory asexcluded
  • Rechtsklick im Dateibaum auf „doc“ → Mark Directory asexcluded

Beide Ordner enthalten Dateien, die nicht von Ihnen stammen. Diese Dateien können Fehler und Hinweise enthalten, veraltete (“deprecated”) Schnittstellen verwenden etc. (Oh ja, auch andere Programmierer machen Fehler. :-) ) Und wenn Sie diese beiden Ordner nicht als „excluded markieren“, prüft WebStorm bei jedem Commit die darin enthaltenen Dateien und weist Sie auf jedes Problem hin. Das können so viele Probleme sein, dass Sie die Probleme in Ihren eigenen Dateien vollkommen übersehen.

Automatische Generierung der Schnittstellenbeschreibung

Installieren Sie zunächst JSDoc:

  • Öffnen Sie das WebStorm-Terminal.
  • npm install -g jsdoc (installiert JSDoc als Node.js-Anwendung; das haben Sie eigentlich schon gemacht, aber das macht nix :-) )
  • npm install ink-docstrap (installiert ein JSDoc-Theme namens „docstrap“ ([1]); das haben Sie auch schon gemacht, aber das macht auch nix.)

Übrigens: Es gibt auch noch andere Themes für JSDoc.

Erstellen Sie im Projektordner „conf“ eine JSON-Datei namens „jsdoc.json“. Fügen Sie in diese Datei folgenden Code ein:

{
  "tags":
  {
    "allowUnknownTags": true
  },
  "source":
  {
    "include":        ["./src/js"],
    "includePattern": ".+\\.js$",
    "excludePattern": "doc"
  },
  "plugins":
  [],
  "templates":
  {
    "systemName":      "WK_HelloWorld04",
    "footer":          "",
    "copyright":       "Copyright © 2016 by Wolfgang Kowarschick, License <a href=\"https://creativecommons.org/licenses/by-nc-sa/4.0/\">CC-BY-NC-SA-4.0</a>",
    "navType":         "vertical",
    "theme":           "cerulean",
    "linenums":        true,
    "cleverLinks":     false,
    "monospaceLinks":  true,
    "collapseSymbols": true,
    "inverseNav":      false
  },
  "opts":
  {
    "template":    "node_modules/ink-docstrap/template",
    "encoding":    "utf8",
    "destination": "doc/jsdoc",
    "recurse":     true,
    "private":     true
  }
}

Ersetzen Sie im Attribut „copyright“ meinen Namen durch Ihren, denn es ist Ihr Projekt, nicht meines. :-)

In dieser Datei wird festgelegt, wie die HTML-Dokumentation des Codes, die JSDoc erstellt, aussehen soll. Insbesondere ist das Template angegeben, das zur Erstellung dieser HTML-Dokumente verwendet werden soll. Es handelt sich um „docstrap“, welches wir zuvor heruntergeladen haben. Von der Projektroot aus gesehen, befinden sich die Template-Dateien im Ordner „node_modules/ink-docstrap/template“.

Weitere Konfigurationsparameter werden unter http://usejsdoc.org/about-configuring-jsdoc.html beschrieben.

Sie können nun eine Dokumentation für Ihr Projekt erstellen:

  • Öffnen Sie das WebStorm-Terminal.
  • jsdoc -c conf/jsdoc.json

Nach kurzer Zeit sollte der Ordner „doc/jsdoc“ erstellt und mit Inhalten gefüllt worden sein. Sehen Sie sich die Datei „doc/jsdoc/index.html“ im Browser an. Diese enthält nicht viel, außer einem Drop-Down-Menü „global“, in dem die drei von Ihnen erstellen globalen Funktionen „sayHello“, „sayHelloOnEnter“ und „init“ enthalten sind. Klicken Sie auf eine dieser Funktionen.

Vermeidung globaler Funktionen

Die Verwendung von globalen Variablen oder Funktionen in JavaScript ist extrem gefährlich. Wenn man eine JavaScript-Datei verwendet, in der globale Funktionen enthalten sind, die zufälligerweise genauso heißen, wie globale Funktionen in der eigenen JavaScript-Datei, kann man nicht beide Dateien laden, da globale Funktionen, die in der ersten Datei enthalten sind, durch globale Funktionen der zweiten Datei überschrieben werden.

Daher ist es besser, Funktionen wie „sayHello“, „sayHelloOnEnter“ und „init“ in ein JavaScript-Objekt einzufügen. In unseren Fall könnte man beispielsweise ein JavaScript-Objekt „main“ erzeugen, dass diese drei Funktionen enthält. Damit hat man nur noch eine globale Variable (main) und die Gefahr einer zufälligen Namenskollision bei Verwendung mehrerer JavaScript-Dateien ist geringer.

Am besten ist es natürlich, auf globale Größen ganz zu verzichten. Dazu braucht man aber ein Modul-System. Dieses ist für EcmaScript 6 geplant ([https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Statements/import import] und export), wird aber derzeit nur von wenigen Browsern unterstützt.

Erserten Sie den Code in Ihrer Datei „main.js“ durch folgenden Code:

/** @namespace main */
var
main =
{
  /**
   * Welcomes the user of the web app by displaying a welcome message
   * that includes his name. The name is fetched from a text input field.
   */
  sayHello:
    function()
    {
      document.getElementById('text_hello').innerHTML =
        'Hello, ' + document.getElementById("input_name").value + '!';
       document.getElementById('section_form').classList.add('hidden');
      document.getElementById('section_welcome').classList.remove('hidden');
    },

  /**
   * An keyboard event observer. It tests whether the enter key has been pressed.
   * If so, the sayHello method is activated. Default reactions of the browser are
   * disabled.
   * @param {KeyboardEvent} p_event - A standard JavaScript keyboard event object
   *   (https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent)
   */
  sayHelloOnEnter:
    function(p_event)
    {
      if (p_event.code === 'Enter' || p_event.keyCode === 13)
      {
        p_event.preventDefault();
        p_event.stopPropagation();
        main.sayHello();
      }
    },

  /**
   * Initializes the web app.
   * Is to be called when the web app has been loaded completely.
   */
  init:
    function()
    {
      document.getElementById('button_submit')
              .addEventListener('click', main.sayHello);

      window.addEventListener('keydown', main.sayHelloOnEnter);
    }
};

// Call main.init, when you are ready with loading.
window.addEventListener('load', main.init);

Mit dem JSDoc-Kommentar „@namespace main“ wird angezeigt, dass die globale Variable „main“ als „Namensraum“ fungiert. Das heißt, main enthält diverse Funktionen und evtl. andere Elemente, die irgendeinen inneren Zusammenhang haben.

Wen Sie jetzt JSDoc erneut ausführen, beschreibt Ihre Dokumentation keine globale Funktionen mehr, sondern einen Namensraum „main“, der die Funktionen „sayHello“, „sayHelloOnEnter“ und „init“ enthält. Auf diese Funktionen kann mittels „main.sayHello“, „main.sayHelloOnEnter“ und „main.init“ zugegriffen werden.

Erstellung der JSDoc-Dokumentation mittels Klickibunti

Falls Sie die Dokumentation nicht immer über das Terminal erstellen möchten, können Sie in WebStorm einen neuen Menüeintrag erstellen, über den Sie die Erzeugung der HTML-Dokumentation starten können.

Sie müssen dazu den Pfad ermitteln, unter dem Ihre jsdoc-Datei zu finden ist. Unter Unix (Mac OS X, Linux, FreeBSD ...) lautet der Befehl, den Sie in ein Terminal eingeben müssen:

which jsdoc

Unter Windows funktioniert dieser Befehl nur in der Git BASH. Außerdem müssen Sie unter Windows den Bash-Pfad modifizieren, damit auch Windows etwas damit anfangen kann. Bei mir gibt dieser Befehl Folgendes aus:

/c/Users/kowa/AppData/Roaming/npm/jsdoc

Um jsdoc in WebStorm aufrufen zu können, müssen Sie in diesem Pfad „/c“ durch „c:“ und „jsdoc“ durch „jsdoc.cmd“ ersetzen. (Mac-User brauchen nichts zu ändern. :-) )

Erstellen Sie nun den neuen WebStorm-Menü-Eintrag:

  • FileSettingsToolsExternal Tools
  • Klick auf das Plus-Icon
    • Name: JSDoc
    • Group: Documentation
    • Description: Generates JavaScript documentation
    • Hacken bei allen Optionen (Options)
    • Hacken bei allen Show-in-Optionen (Show i) mit Ausnahme von „Search results
  • Program: c:/Users/kowa/AppData/Roaming/npm/jsdoc.cmd (hier kommt Ihr zuvor ermittelter Pfad hinein)
  • Parameters: -c $ProjectFileDir$/conf/jsdoc.json
  • Working directory: $ProjectFileDir$
  • OK
  • OK

Nun finden Sie an mehreren Stellen in WebStorm einen neuens Menüpunkt Documentation mit einem Unterpunkt JSDoc:

  • Hauptmenü : ToolsDocumentationJSDoc
  • Rechtsklick auf Datei im Projekt-Dateibaum → DocumentationJSDoc
  • Rechtsklick auf Datei im Editor-Tab-Menü → DocumentationJSDoc

Wenn Sie auf einen dieser Unterpunkte „JSDoc“ klicken, erhalten Sie entweder eine Fehlermeldung ( :-( ) oder es wird eine neue Dokumentation erstellt (d. h., Sie haben alles richtig gemacht :-) ).

Um sicher zu gehen und um nicht mehr verwendete Dokumentationsdateien wie „doc/jsdoc/global.html“ zu entfernen, sollten Sie den Ordner „doc/jsdoc“ gemeinsam mit seinem gesamten Inhalt löschen und JSDoc nochmals aufrufen.

Sicherung im Repository

Bevor Sie weitermachen: Haben Sie Ihre aktuelle Version von Hello World 04 schon im Repository gesichert?

Bevor Sie die Projekt-Dateien per Commit ins Repository übertragen, sollten Sie überprüfen, dass der Ordner „node_modules“ auf der sogenannten „Ignore List“ steht (und damit nicht ins Repository eingespielt wird):

  • Files bzw. WebStormSettings bzw. PreferencesIgnored Files
  • Wenn in dieser Liste der Ordner „node_modules“ nicht aufgeführt wird, fügen Sie ihn mittels des Plus-Icons ein.

Sie sollten allerdings den Ordner „doc“ zu Ihrem Repository hinzufügen:

  • Klick auf „doc“ im Dateibaum → SubversionAdd to VCS

Nun können Sie Ihr Projekt committen.

Automatische Komprimierung von CSS- und Javascript-Dateien

Nun folgt die letzte Optimierung der Anwendung. Die von Ihnen erstellten CSS- und Javascript-Dateien enthalten viele Leerzeichen, Kommentare, „sprechende“ anstelle von kurzen Variablenbezeichnern etc. Das ist für Sie und alle anderen Entwickler von Vorteil. Für die Übertragung der Daten zu einem Client (Browser) bedeutet das nur unnötiges Datenvolumen. Gerade für mobile Tarife mit Volumenbegrenzung sollten so wenig Daten wie möglich übertragen werden.

Glücklicherweise gibt es Tools, die bestehende CSS- oder Javascript-Dateien komprimieren (meist sogar so, dass sich die Semantik der Dateien nicht ändert; aber wie gesagt: Auch andere Programmierer machen hin und wieder Fehler).

Wir verwenden die Node.js-Tools „cssmin“ für CSS-Dateien (https://www.npmjs.com/package/cssmin) und „UglifyJS2“ für JavaScript-Dateien (https://github.com/mishoo/UglifyJS2). Gesteuert werden diese “minimizer” oder “minifier” mittels dem bereits installierten JavaScript Task Runner grunt“.

Die für das Minimieren mittels Grunt notwendige Pakete wurden ebenfalls bereits installiert:

npm install grunt grunt-contrib-watch grunt-contrib-cssmin grunt-contrib-uglify --save-dev

Nun müssen die Aufgaben zu Komprimieren der JavaScript- und CSS-Dateien in die Konfigurationsdatei eingefügt werden (vgl. http://gruntjs.com/getting-started):

gruntfile.js

module.exports = function(grunt)
{
  // Project configuration.
  grunt.initConfig
  ({
    pkg: grunt.file.readJSON('package.json'),

    cssmin:
    { options: /* https://github.com/jakubpawlowicz/clean-css */
      { roundingPrecision:  -1,
        keepSpecialComments: 1
      },
      minimize:
      { files:
        { 'web/css/main.css': ['src/css/terminal.css', 'src/css/main.css']
        }
      }
    },

    uglify:
    { options: /* https://github.com/gruntjs/grunt-contrib-uglify */
      { mangle:           true,
        preserveComments: function(p_node, p_comment)
                          { return /@license/.test(p_comment.value); }
      },
      minimize:
      { files: { 'web/js/main.js': ['src/js/terminal.js', 'src/js/main.js']
               }
      }
    }
  });

  // Load the plugin that provides the "uglify" task.
  grunt.loadNpmTasks('grunt-contrib-uglify');
  grunt.loadNpmTasks('grunt-contrib-cssmin');

  // Default task(s).
  grunt.registerTask('default', ['uglify', 'cssmin']);
};

Die Funktion „grunt.initConfig“ aufgerufen und es wird ihr ein JavaScript-Objekt übergeben. Im Attribut „pkg“ wird grund der Inhalt der Datei „package.json“ übergeben, damit Grunt ermitteln kann, welche Pakete verfügbar sind. Die beiden anderen Attribute „cssmin“ und „uglify“ (deren Namen frei wählbar sind), werden die beiden Minifier konfiguriert.

In beiden Fällen können Optionen angegeben werden, wie z. B., dass CSS-Maß-Angaben nicht gerundet werden sollen (es gibt Designer, die bestehen beim Pixelmaß auf vier Nachkommastellen) oder dass geeignete JavaScript-Variablen-Namen „durch die Mangel gedreht“ (mangle), also durch kürze Namen ersetzt werden sollen. Wichtiger ist jedoch die Angabe, welche Dateien überhaupt erstellt werden und was für Sourcedaten dafür verwendet werden solle.

Zu jeder Datei, die erstellt wird, kann man eine ganze Liste von Sourcedateien angeben. Das hat zur Folge, dass alle diese Sourcedateien nacheinander komprimiert und dann in die Zieldatei eingefügt werden. Auch das ist eine Optimierung, um das Laden einer Web-Anwendung zu beschleunigen. Eine große Datei wird per HTTP schneller übertragen als viele kleine. Künftig werden wir daher möglichst wenige komprimierte Dateien verwenden und möglichst viele nicht-komprimierte JavaScript-Dateien in komprimierter Form nacheinander in nur eine einzige JavaScript-Datei einfügen.

Nach der Initialisierungsfunktion werden noch zwei weitere Funktionen aufgerufen. Mit grunt.loadNpmTasks werden die Grunt-Plugins geladen, die die eigentliche Komprimierung vornehmen. Und mittels grunt.loadNpmTasks wird festgelegt, welche Aufgaben standardmäßig ausgeführt werden, sobald der Befehl „grunt“ ausgeführt wird.

So, nun sollte alles funktionieren. Öffnen Sie das Web-Storm-Terminal und tippen Sie nacheinander folgende Befehle ein:

grunt cssmin
grunt uglify
grunt

Sie werden feststellen, das jedes mal bestimmte Dateien in den Ordnern „web/css“ oder/und „web/js“ erstellt werden, die mit dem Namensbestandteil „min“ beginnen. Sehen Sie sich die erzeugten Dateien an. Sie enthalten denselben Code wie die Original-Dateien, nur in komprimierter (und daher für den Menschen schwer lesbarer) Form.

Als nächstes muss die Datei „index.html“ angepasst werden. Diese lädt noch die unkomprimierten Dateien aus dem Sourceverzeichnis. Künftig soll sie die komprimierten Dateien aus dem eigentlichen Web-Ordner laden:

...
<link rel="stylesheet" type="text/css" href="css/main.css"/>
...
<script type="text/javascript" src="js/main.js"></script>

Ab sofort werden nicht mehr die Sourcedateien geladen, sondern die komprimierten, durch die Minifier erstellten Dateien. Das heißt aber für uns, dass wir bei jeder Änderung an den Sourcedateien den Befehl „grunt“ ausführen müssen, um die Aktualisierungen in die komprimierten Versionen der JavaScript bzw. CSS-Dateien zu übertragen.

Das ist lästig und geht besser.

Automatische Aktualisierung der komprimierten Dateien

Mit Hilfe des Node.js-Paketes „grunt-contrib-watch“ kann man beliebige Dateibäume überwachen, ob sich darin bestimmte Dateien ändern. In einem derartigen Fall kann man automatisch andere Grunt Tasks ausführen lassen. Das lässt sich ausnutzen, um nach Änderungen einer CSS-Datei oder einer JavaScript-Datei die zugehörige komprimierte Datei automatisch neu zu erstellen.

Öffnen Sie das WebStorm-Terminal und laden Sie das besagte Paket:

npm install grunt-contrib-watch --save-dev

Erweitern Sie nun die Datei „gruntfile.js“ um einen Watch-Task:

watch:
{ css:
  { files:   ['src/css/**/*.css'],
    tasks:   ['cssmin']
  },
  js:
  { files:   ['src/js/**/*.js'],
    tasks:   ['uglify']
  },
  web:
  { files:   ['web/**/*'],
    options: { livereload: true,
               spawn:      false
             }
   }
}

Vergessen Sie nicht, das Plugin zu laden:

grunt.loadNpmTasks('grunt-contrib-watch');

Der Watch-Task überwacht drei verschiedene Ordner:

  • Wann immer sich eine CSS-Datei im Ordner „src/css“ oder einem darin enthaltenen Unterordner ändert, wird der Task „cssmin“ ausgeführt.
  • Wann immer sich eine JavaScript-Datei im Ordner „src/js“ oder einem darin enthaltenen Unterordner ändert, wird der Task „uglify“ ausgeführt.
  • Wann immer sich irgendeine Datei im Web-Ordner ändert, wird eine livereload-Nachricht (http://livereload.com/) an einen lokalen Node-Server mit der Portnummer 35729 geschickt. (Keine Angst: Sie brauchen diesen Server nicht zu starten oder zu stoppen.)

Damit kann man Browser anweisen, die HTML-Dateien neu zu laden, wann immer sich etwas ändert. Dies erfolgt entweder mit speziellen Browser-Plugins (http://livereload.com/extensions/) oder mit einem kleinen JavaScript-Hack: Fügen Sie ans Ende der Datei „main.js“ folgenden Code ein:

// Enable live reloading for the WebStorm web server.
if (location.port === '63342')  // WebStorm server port
{
  var l_script = document.createElement('script');
  l_script.src = 'http://'+(location.host||'localhost').split(':')[0]+':35729/livereload.js';
  document.body.appendChild(l_script);
}

Dieser Code sorgt dafür, dass in eine HTML-Datei, die die Datei „main.js“ einbindet, ein zusätzliches script-Element

<script src="http://localhost:35729/livereload.js></script>

erstellt und in jede HTML-Datei eingefügt wird, die von einem WebStorm-Test-Server ausgeliefert wird. (Der URI kann anstelle des Servernamens „localhost“ auf den echten Namen des Entwicklungsrechners enthalten, sofern der Entwicklungsrechner einen derartigen Namen hat.)

Unter Port 35729 wird auf dem Entwicklungsrechner von grunt-contrib-watch der bereits erwähnte Livereload-Server gestartet und die JavaScript-Datei „livereload.js“ zur Verfügung gestellt. Wenn man diese Datei in seine HTML-Datei einbindet, aktualisiert sich die HTML-Datei fortan automatisch, sobald grunt-contrib-watch eine Änderung, wie z. B. die Aktualisierung einer CSS-Datei, einer JavaScript-Datei, der HTML-Datei selbst oder einer anderen Datei, die von der HTML-Datei eingebunden wird, meldet.

Allerdings wäre es falsch, livereload.js auf einem Live-Server ohne Testumgebung einzubinden. Dort läuft der Livereload-Server überhaupt nicht. Daher wird zunächst ermittelt, über welchen Port die aktuelle HTML-Datei von Server ausgeliefert wurde. Üblicherweise ist dies 80, 443, 8080, 8888 oder 8443. Andere Ports werden selten verwendet. Der Port 63342 wird mit fast absoluter Sicherheit nicht von irgendeinem realen Server verwendet. Daher verwendet ihn WebStorm. Und daher kann mit dem Test if (location.port == 63342) ermittelt werden, ob die aktuelle HTML-Datei von einem WebStorm-Server ausgeliefert wurde oder von einem anderen Server. (Die globale JavaScript-Variable „location“ enthält den URI, über den die HTML-Datei vom Browser geladen wurde.)

Nun ist es an der Zeit, den grunt-contrib-watch zu testen. Öffnen Sie „index.html“ im Browser (Rechtsklick auf index.htmlRun 'index.html') und tippen Sie dann

grunt watch

ins WebStorm-Terminal. Ändern Sie nun einen Eintrag in der Datei „main.css“ (z. B. font-family: "Comic Sans MS"; :-) ). Wenn Sie diese Änderungen speichern (Strg-S, Apfel-S), sollten Sie zweierlei Reaktionen bemerken. Zum einem wird ins Terminal die Meldung

>> File "web/css/main.css" changed.

geschrieben und zum anderen ändert sich das Layout der HTML-Datei im Browser, da die aktualisierte CSS-Datei automatisch vom Browser nachgeladen wurde.

Wenn Sie die Designsünde „Comic Sans MS“ rückgängig machen, ändert sich die Datei kurz darauf auch im Browser wieder.

Das ist schon ziemlich „cool“, hat aber noch einen Schönheitsfehler: Das WebStorm-Terminal kann nicht anderweitig genutzt werden, solange der Grunt-Watcher läuft. Um dieses Problem zu beheben, wird ein WebStorm-Startup-Task definiert, der den Watcher bei jedem Programmstart von WebStorm automatisch in einem eigenständigen Task startet. Der Watcher läuft dann, solange WebStorm geöffnet ist. Für seine Ausgaben steht im ein eigenes Ausgabefenster zur Verfügung. Was will man mehr?

  • FileSettingsToolsStartup Tasks
  • Klick auf grünes Plus-Icon → Add New ConfigurationGrunt.js
    • Name: Watch
    • Gruntfile: .../HelloWorld04/gruntfile.js (sollte bereits korrekt eingetragen sein)
    • Tasks: watch
    • Der Node-Interpreter und das Node.js-Paket grunt-cli sollten ebenfalls bereits korrekt eingetragen sein.
  • OK (Unter „Run configuration“ sollte jetzt „Watch“ stehen.)
  • OK

Wenn Sie nun WebStorm neu starten, sollte und der Menüzeile am unteren Fensterrand ein Tabulator mit dem Namen „Run“ vorhanden sein. Wenn Sie das zugehörige Fenster öffnen sollte dort Folgendes stehen:

grunt watch
Running "watch" task
Waiting...

Sobald Sie irgendeine Änderung an eine web- oder src-Datei der Web-App vornehmen, werden alle im Watcher definierten Aktionen (automatische Komprimierung bzw. Neuladen des HTML-Datei im Browser) automatisch ausgeführt. Der Watcher läuft solange, bis Sie WebStorm beenden. Es erfolgt dabei sogar noch eine Sicherheitsabfrage, on Sie den Prozess „Watch“ beenden wollen.

JSHint

Ein weiteres sehr interessantes Tool, das den JavaScript-Entwickler unterstützt, ist JSHint (http://jshint.com/). JSHint analysiert vorhandenen JavaScript-Code und weist auf Fehler und Strukturprobleme hin.

Installieren Sie zunächst die zugehörigen Node.js-Pakete:

npm install grunt-contrib-jshint jshint-stylish --save-dev

Anschließend fügen Sie einen JSHit-Task in die Datei „gruntfile.js“ ein.

jshint:
{
  all: ['gruntfile.js', 'src/**/*.js'],

  options:
  {
    esversion: 5,
    eqeqeq:    true,
    eqnull:    true,
    browser:   true,
    reporter:  require('jshint-stylish')
  }
}


Nun können alle vom Entwickler erstellten JavaScript-Dateien analysiert werden. Geben Sie dazu im WebStorm-Terminal einfach folgenden Befehl ein:

grunt jshint

Gut ist es, wenn JSHint mit der Meldung v No problems antwortet. Ansonsten sollten Sie in Ihrem Code die von JSHint gemeldeten Probleme beheben.

Machen Sie ruhig mal die Probe auf's Exempel: Ersetzen Sie in der Datei „main.js“ „===“ durch „==“ und lassen Sie danach JSHint laufen. Dann sollte JSHint Folgendes melden:

Expected '===' and instead saw '=='.

Damit wird ausgedrückt, dass ein nicht-strikter Gleichheitstest verwendet wurde, aber ein strikter Gleichheitstest verwendet werden sollte. Der Grund ist, dass nicht-strikte Fehlertests manchmal subtile Fehler zur Folge haben, die nur sehr schwer zu entdecken sind.

JSHint kennt zahlreiche Optionen, die festlegen, welche Fehler und Probleme gemeldet werden sollen und welche nicht: http://jshint.com/docs/options/. Hier muss jeder Programmierer seinen eigenen Stil finden.

Folgende Optionen wurden im obigen Beispiel verwendet:

esversion: 5
Des sollen die Regeln von EcmaScript 5 beachtet werden.
eqeqeq: true
Es sollt stets ein strikter Gleichheitstest verwendet werden.
eqnull
Der nicht-strikte Gleichheitstest ... == null ist allerdings erlaubt.
browser: true
Globale Variable sollte man eigentlich grundsätzlich nicht verwenden. Mit dieser Option werden für die globalen Variablen, die die Browser bereitgestellt werden (document, window, console etc) Ausnahmen definiert.

Anmerkung: Wäre der nicht-strikte Gleichheitstest auf „null“ nicht erlaubt, müsste man in viele Fällen „

if (... === null || ...=== undefined)

schreiben. Geben Sie folgende Befehle einfach mal in die Konsole Ihres Web-Browsers ein (Firefox: Strg-Shift-K, Opera und Chrome: Strg-Shift-IConsole):

var a;
var b = null;

// Strikte Gleichheitstests
console.log(a === null);                     /* -> false */
console.log(a === undefined);                /* -> true  */
console.log(b === null);                     /* -> true  */
console.log(b === undefined);                /* -> false */
console.log(a === null || a === undefined);  /* -> true  */
console.log(b === null || b === undefined);  /* -> true  */

// Nicht-strikte Gleichheitstests
console.log(a == null);                      /* -> true  */
console.log(a == undefined);                 /* -> true  */
console.log(b == null);                      /* -> true  */
console.log(b == undefined);                 /* -> true  */

Zum Abschluss können Sie entweder einen WebStorm-Menü-Eintrag hinzufügen, um den JSHint jederzeit per Klickibunti starten zu können. Oder Sie fügen gleich „jslint“ in die Watch-Liste ein, damit jede Änderung sofort analysiert und Fehler sofort angezeigt werden:

gruntfile.js

watch:
{ ...
  js:
  { files:   ['src/js/**/*.js'],
    tasks:   ['uglify', 'jshint']
  },
  ...
}

Allerdings kann JSHint manchmal nerven. :-) Dagegen helfen dann spezielle Kommentare im JavaScript-Code, die JSHint anweisen für die Prüfung des nachfolgenden Codes spezielle Optionen zu benutzen, die dann i. Allg. eine etwas weniger strenge Überprüfung des Codes zur Folge haben (http://jshint.com/docs/).

Quellen

  1. Kowarschick (MMProg): Wolfgang Kowarschick; Vorlesung „Multimedia-Programmierung“; Hochschule: Hochschule Augsburg; Adresse: Augsburg; Web-Link; 2018; Quellengüte: 3 (Vorlesung)