Das Titelthema im ADMIN 04/14 "Vernetzt speichern" sind Netzwerkdateisysteme, etwa Samba 4, verteilter Storage mit Ceph & GlusterFS und der Unix-Klassiker ... (mehr)

Templates

Auf so primitive Weise HTML auszugeben, funktioniert natürlich auch, aber dafür gibt es heutzutage bessere Wege, nämlich Templates. Auch Node und Express bieten eine Vielzahl von Template-Engines, über die sich das Layout von Seiten vorgeben lässt, die man zur Laufzeit mit Inhalt füllt. Besonders kompakt sind beispielsweise Jade-Templates, die ganz auf spitze Klammern sowie schließende Tags verzichten und stattdessen die Einrückungstiefe zur Strukturierung verwenden. Ein Jade-Template kann damit etwa so aussehen:

html(lang="de")
  body
  h1 Hello

Wie man sieht, ist hier einiges an Tipparbeit zu sparen. Node konvertiert den Jade-Code beim Aufruf in Echtzeit nach HTML, wenn man die Render-Funktion des Response-Objekts mit der Template-Datei als erstem Parameter aufruft. Über die Endung bestimmt Express dabei die Template-Engine. Wenn sie fehlt, greift Express auf die Default-Engine zurück, die ein Aufruf von »app.set('view engine', 'jade')« festlegt. Wer keine Lust hat, mit Jade noch eine weitere Sprache zu lernen, kann hier etwa mit »swig« eine Template-Syntax einstellen, die klassisches HTML um Variablen bereichert [7]. Per Default suchen Express-Anwendungen die Templates im Unterverzeichnis »views« . Wer einen anderen Ort einstellen möchte, kann dies mit »app.set('views', Verzeichnis)« tun. Dabei ist die globale Variable »__dirname« einer Javascript-Node-Datei nützlich. Sie enthält das Verzeichnis, in dem sich die Datei befindet.

Interessant werden Templates natürlich erst, wenn man ihnen auch Variablen übergibt, sonst könnte man gleich statische HTML-Dateien schreiben. In Swig-Templates werden Variablen mit je zwei geschweiften Klammern links und rechts eingeschlossen. Darüber hinaus gibt es auch Filter, Loops, Funktionen und Vererbung von Templates. Letzteres ist interessant, um beispielsweise den HTML-Header oder den sichtbaren Footer einer Seite in eine einzige Datei auszulagern und trotzdem auf allen Seiten zu verwenden. Um die Variablen vom Node-Code an das Template zu übermitteln, übergibt man sie als Javascript-Objekt. Ein Beispiel ist in Listing 3 zu sehen. Falls das seltsam aussieht, muss man bedenken, dass das erste Auftreten von »firstname« der Keyname des Objekts ist und das zweite die gleichnamige Javascript-Variable. Im Swig-Template gibt »{{firstname}}« die entsprechende Variable aus.

Listing 3

Template-Rendering

 

Die Daten stammen bei Node-Anwendungen meist aus NoSQL-Datenbanken wie MongoDB, für die es passende NPM-Module gibt. Aber auch klassische relationale Datenbanken wie MySQL, MariaDB und PostgreSQL werden unterstützt. Wer ein bisschen im Netz sucht, findet auch immer wieder interessante Projekte, die den gewohnten Denkrahmen verlassen, wie Bookshelf, das eine objektrelationale Schicht für MySQL, PostgreSQL und SQLite darstellt, die Transaktionen bietet und mit Javascript-Promises ein ungewöhnliches, aber leistungsfähiges Programmiermodell für asynchrone Verarbeitung von An-/Abfragen aufweist.

Struktur

Prinzipiell schreiben weder Node noch Express eine bestimmte Struktur für Webanwendungen vor, was durchaus als Schwäche verstanden werden kann, denn als Einsteiger findet man sich erst einmal etwas orientierungslos wieder. Allerdings bietet Express, wenn man es mit dem Schalter »-g« global installiert, ein gleichnamiges Kommandozeilen-Tool. Es legt für statische Dateien das Verzeichnis »public« an, für Routen »routes« und für Templates »views« . Die Hauptfunktion der Anwendung findet sich in der Datei »app.js« (Abbildung 2).

Abbildung 2: Das Express-Tool legt auf Wunsch die Grundstruktur einer Anwendung an.

In der Abbildung ist ein weiterer Aspekt des NPM-Paket-Managements zu sehen, nämlich die Datei »package.json« . Wer von Hand eine Node-Anwendung entwickelt, muss sie nicht verwenden, aber zur Strukturierung des eigenen Projekts ist sie zu empfehlen. Sie enthält die Metadaten eines Projekts und die Pakete, die nötig sind, um es zu starten. Ein weiterer Abschnitt enthält noch die Pakete, die nötig sind, um das Projekt zu "entwickeln", aber dazu später mehr. Ist »package.json« vollständig, genügt es, im gleichen Verzeichnis »npm« aufzurufen, damit NPM alle darin aufgeführten Pakete einschließlicher ihrer Abhängigkeiten installiert.

Auf der anderen Seite bietet »npm« zwei recht praktische Schalter, die es dem Entwickler ersparen, jedes nötige Paket von Hand in »package.json« einzutragen. »npm install Paket --save« trägt das zu installierende Paket in den Abschnitt »dependencies« in »package.json« ein, während »npm install Paket --save-dev« das Paket in »devDependencies« verortet. Ein Beispiel einer solchen NPM-Paket-Datei ist in Listing  4 zu sehen.

Listing 4

package.json

 

Unter den Development-Dependencies ist hier nur das Grunt-Paket [8] plus ein paar seiner Unterprojekte zu finden, über die noch ein paar Worte zu sagen sind: Um den Entwicklungsprozess weiter zu strukturieren, verwenden auch Javascript- beziehungsweise Node-Entwickler immer öfter Build-Tools, wie sie von compilierten Sprachen bekannt sind, etwa Make/CMake von C/C++ oder Ant/Maven mit Java. Prinzipiell fällt bei Javascript ja der Compile-Schritt weg, den beim Servercode Node übernimmt und beim Clientcode der Webbrowser des Anwenders. Allerdings gibt es in der Webentwicklung dennoch eine ganze Reihe von Schritten, mit denen sich Projekte durch "Compiler" oder Präprozessoren optimieren lassen. Hierbei helfen dann Tools wie Grunt.

So gibt es beispielsweise auch für CSS-Stylesheets etliche Metadialekte wie SASS und LESS, die zum Beispiel Includes und Variablen einführen, ähnlich wie die oben erwähnten Templates für HTML oder Abstraktionen für die Unterschiede zwischen verschiedenen Browsern. Dies vereinfacht die Wartung der Stylesheets in größeren Projekten erheblich. An irgendeiner Stelle vor der Auslieferung an den Browser müssen diese Dialekte dann wieder in klassisches CSS übersetzt werden, entweder zur Laufzeit oder offline mithilfe eines Build-Tools wie Grunt, der wiederum die passenden Präprozessoren aufruft.

Das gleiche gilt für Javascript-Code, der für Entwicklung und Produktion unterschiedlichen Anforderungen genügen muss: Beim Entwickeln stehen Strukturierung, Les- und Wartbarkeit im Vordergrund, während es in der Produktionsphase in erster Linie um Performance geht – dass der Code in beiden Fällen soweit möglich fehlerfrei sein sollte, versteht sich von selbst. Performance bedeutet bei Webprojekten neben effizienten Algorithmen in erster Linie möglichst kleine und möglichst wenige Dateien. Hier gilt es meistens einen Kompromiss zu finden, der sich zum Beispiel durch die vorgegebenen Eckdaten (maximale Zahl gleichzeitiger HTTP-Connections pro Browser/Server-Kombination) ergibt oder experimentell ermitteln lässt. Jedenfalls unterstützt Grunt auch diesen Prozess, indem es dem Entwickler erlaubt, diverse Tools einzubinden, um Javascript-Dateien zu komprimieren (etwa UglifyJS), zusammenzuführen oder anderweitig zu transformieren.

Ähnliche Artikel

comments powered by Disqus

Artikel der Woche

Eigene Registry für Docker-Images

Wer selber Docker-Images herstellt, braucht auch eine eigene Registry. Diese gibt es ebenfalls als Docker-Image, aber nur mit eingeschränkter Funktionalität. Mit einem Auth-Server wird daraus ein brauchbares Repository für Images. (mehr)
Einmal pro Woche aktuelle News, kostenlose Artikel und nützliche ADMIN-Tipps.
Ich habe die Datenschutzerklärung gelesen und bin einverstanden.

Konfigurationsmanagement

Ich konfiguriere meine Server

  • von Hand
  • mit eigenen Skripts
  • mit Puppet
  • mit Ansible
  • mit Saltstack
  • mit Chef
  • mit CFengine
  • mit dem Nix-System
  • mit Containern
  • mit anderer Konfigurationsmanagement-Software

Google+

Ausgabe /2019