Dieser Vortrag soll zeigen wie ein modernes Ökosystem für Frontend Anwendungen aussehen kann. Node.js ist aus dem Altag eines Frontend Entwicklers nicht mehr wegzudenken. AMD und CommonJS kommen immer häufiger vor und wie versteht eigentlich ein nicht ganz so moderner Broweser die neusten Sprachfeatueres von JavaScript?
Video Aufzeichnung (Youtube)
Die Aufzeichnung der Schulung ist auf Youtube nachzusehen:

Christian Schaiter und Mathias Feitzinger
- Node.js
- Node Package Manager
- JavaScript Module System and Bundlers
- WebPack Einführung
Javascript für Server
- Node.js ist eine serverseitige Plattform für die Entwicklung von serverseitigen Programmen direkt in Javascript.
- Läuft als Dienst am Computer (Mac, Windows, Linux etc..) (Download für Windows).
- Ermöglicht die Entwicklung der gesamten Serverumgebung (Bsp.: Trello).
- Enthält mit NPM (Node Packet Manager) einen eigenen Pakete-Manager.
npm install -g hangman-gameLets play 😝 🎮 🎮 🎮 🎮 🎮 🎮 🎮 🎮
Für C# Entwickler: NPM ~ NuGet for Javacript
- Integrierte Paket-Verwaltung von Node.js (wird bei der Installation von Node.js mitinstalliert)
- Erweitertes Dependency-Management System (alle Abhängigkeiten werden mitgeladen)
- Versionssicherheit durch Semantic Versioning (semver)
- Neue Konfigurationsdatei für NPM (package.json) anlegen:
npm init - Live-Server: Einfacher HTTP-Server für die Entwicklung:
npm install -g live-serverbzw. zum Starten:live-server
index.html
<!DOCTYPE html>
<html lang="en">
<head></head>
<body>
<h1>May the force be with you!</h1>
<span>You are </span>
<span id='name'>???</span>
<script src="./main.js" type="text/javascript"></script>
</body>
</html>main.js
'use strict';
function forceMe(){
var nameSpan = document.getElementById('name');
nameSpan.innerHTML = "Franz";
}
forceMe();- Abhängigkeit zu NPM Paket herstellen
npm install random-lastname --save - Verwendung des neuen Paketes in
main.js:
var randomLastname = require("random-lastname");💥ERROR💥 😱😱😱
- Der Browser versteht die obige
requireFunktion nicht. - Es wird also eine Bibliothek benötigt, die diese Funktionalität liefert (Module Loader) oder es muss die Funktionalität bereits beim Kompilieren mitgeliefert werden (Module Bundler).
- Wir verwenden einen einfachen Module Bundler: Browserify
- npm-run: Startet NPM Executables direkt, ohne lästiges Referenzieren in den node_modules Ordner:
npm install -g npm-run - browserify: Stattet obigen Code mit der notwendigen Funktionalität aus:
npm install browserify --save-dev - watchify: Automatische Kompilierung mit Browserify nach jeder Änderungen:
npm install watchify -gbzw. zum Startenwatchify main.js -o static/bundle.js
Da wir nun ein NPM Paket eingebunden haben, müssen wir die HTML-Datei sowie den Javascript-Code entsprechend anpassen:
index.html
<!DOCTYPE html>
<html lang="en">
<head></head>
<body>
<h1>May the force be with you!</h1>
<span>You are </span>
<span id='name'>???</span>
<button onclick="forceMe()">Force me!!</button>
<script src="bundle.js" type="text/javascript"></script>
</body>
</html>main.js
'use strict';
var randomLastname = require("random-lastname");
function forceMe(){
var nameSpan = document.getElementById('name');
nameSpan.innerHTML = randomLastname() + " " + randomLastname();
}
forceMe();
// Required to reference the function from the <button> tag after compilation with Browserify
window.forceMe = forceMe;Mit diesen Änderungen funktioniert der Code wieder und wir haben unser erstes Paket eingebunden.
- Neues Paket installieren:
npm install <PACKAGE_NAME> - Neues Paket installieren und einen Verweis in
package.jsonablegen:npm install <PACKAGE_NAME> --save - Neues Paket für den Entwicklungsprozess installieren:
npm install <PACKAGE_NAME> --save-dev - Paket "global" installieren:
npm install <PACKAGE_NAME> --global - Alle Pakete für das aktuelle Projekt installieren (Informationen kommen aus
package.json):npm install - Alle Pakete für das aktuelle Projekt updaten (Informationen kommen aus
package.json):npm update
Semantic versioning (semver) Image Sources
- Man installiert auf dem Entwicklerrechner mit NPM die aktuelle Version und arbeitet dann ohne Update einige Zeit damit.
- Wenn dann bei der Produktivsetzung am Build-Server der Frontend-Build ausgeführt wird, kann man eine andere (neuere) Version bekommen.
- Sollte diese Version Breaking-Changes oder einen Bug haben, fällt dies erst in der Produktion auf (getestet wurde mit der lokal installierten Version)
Schreiben von Vanilla Javacsript Files:
calculator.js
var calculator = function () {
function add(summand1, summand2) {
logger.log("Add " + summand1 + "+" + summand2);
return summand1 + summand2;
}
function multiply(factor1, factor2) {
logger.log("Multiply " + factor1 + "*" + factor2);
return factor1 * factor2;
}
function modulo(dividend, divisor) {
logger.log("Modulo " + dividend + "%" + divisor);
return dividend % divisor;
}
return {
add: add,
multiply: multiply,
modulo: modulo
}
}();domWriter.js
var domWriter = function () {
function write(selector, input) {
logger.log("Write " + input + " to inner html of selector " + selector);
var nameSpan = document.getElementById(selector);
nameSpan.innerHTML = input;
}
return{
write: write
}
}();logger.js
var logger = function () {
function logMessage(message) {
console.log(message);
}
return {
log: logMessage
}
} ();main.js
var calculate = function () {
var addResult = calculator.add(100, 400);
domWriter.write('add-result', addResult);
var multiplicationResult = calculator.multiply(4, 8);
domWriter.write('multiplication-result', multiplicationResult);
var moduloResult = calculator.modulo(25, 4);
domWriter.write('modulo-result', moduloResult);
};Verwendung dieser im Html: index.html
<!DOCTYPE html>
<html lang="en">
<head>
<link rel="stylesheet" type="text/css" href="./node_modules/bootstrap/dist/css/bootstrap.css">
</head>
<body class="container">
<div class="jumbotron">
<h1>Calculation Results</h1>
<ul>
<li>Addition 400 + 100 = <span id="add-result">???</span></li>
<li>Multiplication 4 * 8 = <span id="multiplication-result">???</span></li>
<li>Modulo 25 % 4 = <span id="modulo-result">???</span></li>
</ul>
<button onclick='calculate()' class="btn btn-primary">Calculate Result</button>
</div>
<script src="logger.js"></script>
<script src="calculator.js"></script>
<script src="domWriter.js"></script>
<script src="main.js"></script>
</body>
</html>Fertiges Beispiel ist mit git checkout Sample_02Completed erreichbar!
- Alles im Global Scope 💥 💥. Somit Probleme mit anderen Modulen
- Manuelle Dependency Resolution notwendig
Modulares JavaScript leicht(er) und wartbarer gemacht.
Es existieren verschiedene Modul-Formate für JavaScript:
- AMD (Asynchronous Module Definition): Ein sehr bekanntes und weit verbreitetes Modul-Format.
- CommonJS (das Standardformat in der serverseitgen Node.js Entwicklung)
- UMD (Universal Module Definition): Untersützt sowohl AMD als auch CommonJS, ist allerdings nicht weit verbreitet.
- System.register: Ein Format für den SystemJS Module Loader.
- ES2015 (ECMAScript 2015): Der eingebaute Standard in der aktuellen JavaScript Version.
Nachdem der JavaScript in einem bestimmten Format geschrieben wurde, braucht es eine spezielles Framework, um das Format zu "verstehen". Diese Frameworks werden Module Loaders genannt.
Folgende populäre Module Loaders finden sich in größeren JavaScript Projekten:
- Require.js: Unterstützt das AMD Format.
- SystemJS: Unterstützt sowohl AMD, CommonJs, ES2015 und System.register.
Die Abhängigkeiten werden zur Laufzeit aufgelöst und nachgeladen, was Vorteile (schnellerer Applikationsstart) aber auch Nachteile (viele Folgerequests) mit sich bringt.
Die Auflösung der Abhängigkeiten kann nicht nur zur Laufzeit, sondern auch bereits zur Kompilierzeit erfolgen. Diese Strategie verfolgen Module Bundlers (die Module werden in eine oder wenige Dateien gebündelt).
Folgende populäre Module Bundlers finden sich in größeren JavaScript Projekten:
- Browserify: Wird dafür verwendet, um das (speziell serverseitig verwendete) CommonJS Format für den Browser zu bundeln (deshalb der Name).
- WebPack: Ein sehr universieller Module Bundler, der verschiedene Formate versteht und sogar andere Medien (wie css, Bilder, etc) bündeln kann.
Hier werden wir im Speziellen vorwiegend auf WebPack eingehen.
AMD ist das Format für Browseranwendungen!
Module werden mit define() definiert.
define(['\logger'], function(logger){
var flyToTheMoon = function(){
logger.log('So Long, and Thanks for All the Fish');
}
return {
flyToTheMoon: flyToTheMoon
}
});- Alle Module in AMD umschreiben
- RequireJs installieren
npm install requirejs --save - index.html umschreiben
- Global Scope wird nicht mehr verwendet
- Dependencymanagement wird von RequireJs übernommen!
- Keine Namenskonflikte möglich!
Fertiges Beispiel ist mit git checkout Sample_03Completed erreichbar!
CommonJS ist das Module System für node.js Anwendungen.
Öffentliche Methoden werden, innerhalb eines Modules, mit einer Zuweisung
zu export oder module.export bekannt gegeben.
Abhängigkeiten können mit require() angegeben werden.
var logger = require('logger.js');
var flyToTheMoon = function(){
logger.log('So Long, and Thanks for All the Fish');
}
exports.flyToTheMoon = flyToTheMoon
}
});- Alle Module in CommonJS umschreiben
- SystemJS installieren
npm install Systemjs --save - index.html umschreiben
Fertiges Beispiel ist mit git checkout Sample_04Completed erreichbar!
ES2015 oder TypeScript haben ein umfangreiches Modulsystem. Jedoch versteht aktuell noch kein aktueller Browser ES2015 und Typescript muss immer in JavaScript komiliert werden. Deshalb muss also der Code immer in eine ECMAScript-Version, die aktuelle Browser verstehen, transpiled werden.
* [babel](https://babeljs.io/) ES15 Support für Browser
* [tsc](https://www.typescriptlang.org/) Compiler für TypeScript
- Umschreiben der Scripte von *.js zu *.ts
modulekeywordexportkeywordimportkeyword
- TypeScript hinzufügen:
npm install typescript --save-dev - TypeScript zu JavaScript compilieren
tsc main.ts -outDir build- Resultat wird in CommonJS ausgegeben
- System.js für Module Loading
npm install systemjs --save
Fertiges Beispiel ist mit git checkout Sample_05Completed erreichbar!
Die bisherigen Lösungen hatten immer den Nachteil, dass sie der Browser für jedes Module eine eigene JavaSCript Datei laden musste. Das kann sehr schnell zu Problemen führen, da Module ja eher klein gehalten werden und die Anzahl an vorhanden Modulen sehr schnell steigt.
Aus diesem Grund werden Module Bundler verwendet. Diese Bundeln alle notwendigen JavaScript Dateien zu einem großen JavaSCript. Der Browser muss, im besten Fall, somit nur eine einzige JavaScript Datei laden.
- Browserify (siehe sample_01)
- Webpack
Bündelt alle Resourcen für ein Web Projekt zusammen. Von Javascript über CSS bishin zu Bildern, Fonts etc..
WebPack erstellt sich ausgehend von einem Einsprungpunkt einen Dependency-Graph und wendet für jede gefundene Datei bestimmte Transformationen (definiert durch sogenannte Loader) an.
In der entry Eigenschaft kann man angeben, wo der Startpunkt für das Bundeling erfolgen soll.
module.exports = {
entry: './main'
}Die output Eigenschaft definiert die Zieldatei, in welcher der gesamte gebündelte Code als Ergebnis zu finden ist.
module.exports = {
entry: './main',
output: {
path: 'build',
filename: 'bundle.js'
}
}Wenn WebPack durch den Dependency-Graph iteriert, wird jede Datei (jeder Knoten) mit den angegebenen (in der gleichen Reihenfolge) Loadern transformiert. So kann z.B. aus einem TypeScript-File eine JavaScript Datei erzeugt werden, bevor sie gebundelt wird.
module.exports = {
entry: './main',
output: {
path: 'build',
filename: 'bundle.js'
},
module: {
loaders: [
{ test: /\.ts$/, loader: 'ts-loader' }
]
}
}Die Transformationen durch Loaders erfolgen nur auf einzelne Files (im Dependency-Graph). Soll jedoch das Resultat transformiert werden (z.B. Minifying), kommen Plugins zum Einsatz. WebPack bietet hierfür eine Vielzahl an verschiedenen Plugins.
module.exports = {
entry: './main',
output: {
path: 'build',
filename: 'bundle.js'
},
module: {
loaders: [
{ test: /\.ts$/, loader: 'ts-loader' }
]
},
plugins: [
new webpack.optimize.UglifyJsPlugin()
]
}- WebPack installieren:
npm install webpack --save-dev: - TypeScript-Loader installieren:
npm install ts-loader --save-dev - WebPack ausführen:
npm-run WebPack


