HTML5-Tutorium: JavaScript: Hello World 04: Unterschied zwischen den Versionen

aus GlossarWiki, der Glossar-Datenbank der Fachhochschule Augsburg
Kowa (Diskussion | Beiträge)
Keine Bearbeitungszusammenfassung
Kowa (Diskussion | Beiträge)
Keine Bearbeitungszusammenfassung
 
(421 dazwischenliegende Versionen von 6 Benutzern werden nicht angezeigt)
Zeile 1: Zeile 1:
{{HTML5-Tutorium:JavaScript:HelloWorld:Menü}}
{{HTML5-Tutorium:JavaScript:HelloWorld:Menü}}
'''Musterlösung''': [https://glossar.hs-augsburg.de/beispiel/tutorium/es5/hello_world/WK_HelloWorld04/index.html <code>index.html</code>]
 
([https://glossar.hs-augsburg.de/beispiel/tutorium/es5/hello_world/WK_HelloWorld04/ SVN-Repository])
'''Musterlösung'''<br/>
v01_1: [https://glossar.hs-augsburg.de/beispiel/tutorium/2025/wk_hello_world/wk_hello_world_04/web/v04_1/index.html <code>index.html</code>] ([https://validator.nu/?doc=https%3A%2F%2Fglossar.hs-augsburg.de%2Fbeispiel%2Ftutorium%2F2025%2Fwk_hello_world%2Fwk_hello_world_04%2Fweb%2Fv04_1%2Findex.html HTML validate], [https://css-validator.org/validator?uri=https%3A%2F%2Fglossar.hs-augsburg.de%2Fbeispiel%2Ftutorium%2F2025%2Fwk_hello_world%2Fwk_hello_world_04%2Fweb%2Fv04_1%2Findex.html&profile=css21&usermedium=all&warning=1&lang=de CSS validate], [https://pagespeed.web.dev/analysis?url=https%3A%2F%2Fglossar.hs-augsburg.de%2Fbeispiel%2Ftutorium%2F2025%2Fwk_hello_world%2Fwk_hello_world_04%2Fweb%2Fv04_1%2Findex.html Google PageSpeed])<br/>
v01_2: [https://glossar.hs-augsburg.de/beispiel/tutorium/2025/wk_hello_world/wk_hello_world_04/web/v04_2/index.html <code>index.html</code>] ([https://validator.nu/?doc=https%3A%2F%2Fglossar.hs-augsburg.de%2Fbeispiel%2Ftutorium%2F2025%2Fwk_hello_world%2Fwk_hello_world_04%2Fweb%2Fv04_2%2Findex.html HTML validate], [https://css-validator.org/validator?uri=https%3A%2F%2Fglossar.hs-augsburg.de%2Fbeispiel%2Ftutorium%2F2025%2Fwk_hello_world%2Fwk_hello_world_04%2Fweb%2Fv04_2%2Findex.html&profile=css21&usermedium=all&warning=1&lang=de CSS validate], [https://pagespeed.web.dev/analysis?url=https%3A%2F%2Fglossar.hs-augsburg.de%2Fbeispiel%2Ftutorium%2F2025%2Fwk_hello_world%2Fwk_hello_world_04%2Fweb%2Fv04_2%2Findex.html Google PageSpeed])<br/>
v01_3: [https://glossar.hs-augsburg.de/beispiel/tutorium/2025/wk_hello_world/wk_hello_world_04/web/v04_3/index.html <code>index.html</code>] ([https://validator.nu/?doc=https%3A%2F%2Fglossar.hs-augsburg.de%2Fbeispiel%2Ftutorium%2F2025%2Fwk_hello_world%2Fwk_hello_world_04%2Fweb%2Fv04_3%2Findex.html HTML validate], [https://css-validator.org/validator?uri=https%3A%2F%2Fglossar.hs-augsburg.de%2Fbeispiel%2Ftutorium%2F2025%2Fwk_hello_world%2Fwk_hello_world_04%2Fweb%2Fv04_3%2Findex.html&profile=css21&usermedium=all&warning=1&lang=de CSS validate], [https://pagespeed.web.dev/analysis?url=https%3A%2F%2Fglossar.hs-augsburg.de%2Fbeispiel%2Ftutorium%2F2025%2Fwk_hello_world%2Fwk_hello_world_04%2Fweb%2Fv04_3%2Findex.html Google PageSpeed])<br/>
v01_4: [https://glossar.hs-augsburg.de/beispiel/tutorium/2025/wk_hello_world/wk_hello_world_04/web/v04_4/index.html <code>index.html</code>] ([https://validator.nu/?doc=https%3A%2F%2Fglossar.hs-augsburg.de%2Fbeispiel%2Ftutorium%2F2025%2Fwk_hello_world%2Fwk_hello_world_04%2Fweb%2Fv04_4%2Findex.html HTML validate], [https://css-validator.org/validator?uri=https%3A%2F%2Fglossar.hs-augsburg.de%2Fbeispiel%2Ftutorium%2F2025%2Fwk_hello_world%2Fwk_hello_world_04%2Fweb%2Fv04_4%2Findex.html&profile=css21&usermedium=all&warning=1&lang=de CSS validate], [https://pagespeed.web.dev/analysis?url=https%3A%2F%2Fglossar.hs-augsburg.de%2Fbeispiel%2Ftutorium%2F2025%2Fwk_hello_world%2Fwk_hello_world_04%2Fweb%2Fv04_4%2Findex.html Google PageSpeed])<br/>
v01_5: [https://glossar.hs-augsburg.de/beispiel/tutorium/2025/wk_hello_world/wk_hello_world_04/web/v04_5/index.html <code>index.html</code>] ([https://validator.nu/?doc=https%3A%2F%2Fglossar.hs-augsburg.de%2Fbeispiel%2Ftutorium%2F2025%2Fwk_hello_world%2Fwk_hello_world_04%2Fweb%2Fv04_5%2Findex.html HTML validate], [https://css-validator.org/validator?uri=https%3A%2F%2Fglossar.hs-augsburg.de%2Fbeispiel%2Ftutorium%2F2025%2Fwk_hello_world%2Fwk_hello_world_04%2Fweb%2Fv04_5%2Findex.html&profile=css21&usermedium=all&warning=1&lang=de CSS validate], [https://pagespeed.web.dev/analysis?url=https%3A%2F%2Fglossar.hs-augsburg.de%2Fbeispiel%2Ftutorium%2F2025%2Fwk_hello_world%2Fwk_hello_world_04%2Fweb%2Fv04_5%2Findex.html Google PageSpeed])
 
[{{Git-Server}}/kowa/wk_hello_world_04.git Git-Repository]
 
==Anwendungsfälle (Use Cases)==
==Anwendungsfälle (Use Cases)==
Gegenüber dem [[HTML5-Tutorium:_JavaScript:_Hello_World_03|dritten Teil des Tutoriums]]
Gegenüber dem [[HTML5-Tutorium:_JavaScript:_Hello_World_03|dritten Teil des Tutoriums]]
ändern sich die die Anwendungsfälle nicht. Die Anwendung leistet also genau dasselbe wie zuvor.
ändern sich die die Anwendungsfälle nicht. Die Anwendung leistet also genau dasselbe wie zuvor.


In diesem Teil des Tutoriums geht es darum, die Anwendung zu verbessern, so dass sie schneller ausgeliefert werden kann.
In diesem Teil des Tutoriums geht es darum, die Anwendung besser zu strukturieren, {{dh}} zu modularisieren (vgl. [[Programmierprinzipien#Modularit.C3.A4t.2C_Modularity.2C_Teile_und_herrsche.2C_Divide_et_impera|Programmierprinzipien]]).


==Erstellen eines neuen Projektes==
==Modularisierung==


Erstellen Sie ein neues Projekt  „<code>HelloWorld04</code>“ und legen Sie dieses in Ihrem Repository ab.
Eine Anwendung, wie {{zB}} ein HTML5-Spiel, besteht aus diversen unterschiedlichen Komponenten mit ganz unterschiedlichen Aufgaben.
Kopieren Sie anschließend alle Dateien aus dem dritten Teil des Tutoriums, passen Sie den Titel in der HTML-Datei an und committen Sie abermals.
Bei einem Web-Frontend müssen beispielsweise verschieden Komponenten (Buttons, Scrolbars, Textfelder, Datumsfelder etc.) erstellt werden. Bei einem HTML5-Spiel müssen die Spielfiguren, die Spielszenen, die Spiellogik, die Benutzereingaben, die Darstellung das Spiels im Browser etc. erstellt und verwaltet werden, wobei das Erstellen und Verwalten teilweise während der Spielentwicklung und teilweise
während der Spielausführung (Runtime) erfolgt.  


==Erstellen einer Ordnerstruktur==
An der Entwicklung eines Web-Systems oder eines Spiels sind üblicherweise mehrere oder gar viele
Entwickler gleichzeitig beteiligt. Oft müssen einzelnen Komponenten an neue Endgeräte oder Betriebssysteme angepasst werden.
Beispielsweise muss eine Tastatursteuerung durch eine Touch- oder Gestensteuerung ersetzt werden,
wenn eine Anwendung so erweitert werden soll, dass es auch auf einem Smartphone oder Tablet läuft.


Grundsätzlich gilt auch bei der Programmierung: Ordnung ist das halbe Leben.
Das alles ist nur machbar, wenn man die Anwendung modularisiert, {{dh}} in kleine, möglichst unabhängige
Komponenten unterteilt. Ein großer Feind der Modularisierung sind globale Variablen.


Web-Anwendungen werden sehr schnell sehr groß. Also sollte man sich eine geeignete Ordnerstruktur überlegen.
Je mehr Dateien von unterschiedlichen Autoren erstellt werden, desto größer ist die Gefahr, dass es beim Zugriff auf globale Variablen zu Namenskollisionen kommt.
Üblicherweise legt man CSS-Dateien in einen Ordner namens  „<code>css</code>“ und JavaScript-Dateien in einen Ordner namens
Außerdem weiß niemand, welche globale Größe noch benötigt wird und welche nicht. Sicherheitshalber löscht man daher keine dieser Variablen, auch wenn sie vollkommen veraltet sind.  
„<code>js</code>“. Sollten es viele CSS- und/oder JavaScript-Datein werden, legt man im entsprechenden Ordner geeignete Unterordner an.


Legen Sie im Root-Verzeichnis Ihres Projektes folgende Ordner an:
Daher gilt der [[Multimedia-Programmierung: Best Practices#Modularisierung|Grundsatz]]: Verwende so wenig globale Größen wie möglich.
* <code>web</code> (Der Inhalt dieses Ordners kommt später auf einen echten Web-Server.)
* <code>src</code> (Dieser Ordner enthält die unkomprimierten CSS- und JavaScript-Dateien.)
* <code>doc</code> (Dieser Ordner enthält die Projektdokumentation.)
* <code>conf</code> (Dieser Ordner enthält Projektkonfigurationsdateien mit Aus
Legen Sie innerhalb der Ordner „<code>web</code>“ und „<code>src</code>“ jeweils zwei weitere Ordner an: „<code>css</code>“ sowie „<code>js</code>“ .


Öffnen Sie im Editorbereich die Datei  „<code>index.html</code>“ und verschieben sie in denr Ordner „<code>src</code>“.
== Erstellen eines neuen Projekts ==
Verschieben sie anschließend die Dateien „<code>main.css</code>“ und „<code>main.js</code>“
in die passenden Ordner „<code>src/css</code>“ bzw. „<code>src/js</code>“. Sie werden feststellen, dass die Verweise in den Link- und Script-Elementen der
Datei „<code>index.html</code>“ automatisch angepasst werden. Verschieben Sie nun die Datei „<code>index.html</code>“ in den Ordner „<code>dist</code>“.


Das Ergebnis dieser Aktion ist, dass Ihre Web-Anwendung nicht mehr im Root-Verzeichnis des Projektes liegt, sondern (sauber strukturiert) in zwei Unterordnern.
Erstellen Sie ein neues Projekt <code>hello_world_04</code> als Fork von <code>hello_world_03</code> (siehe [[HTML5-Tutorium: JavaScript: Hello World 02|Hello-World-02-Tutorium]]).
Daher ist es möglich, im Root-Verzeichnis des Projektes weitere projektspezifische Ordner und Dateien anzulegen wie „<code>conf</code>“ und „<code>doc</code>“,
die später nicht auf den Web-Server, auf dem die Anwendung schließlich laufen soll, kopiert werden.


==Laden der CSS- und der JavaScript-Dateien==
== Die App ==


Eine Web-Anwendung funktioniert nur – wie Sie bereits erfahren haben –, wenn nicht nur die HTML-Datei, sondern auch die zugehörigen
=== Erstellen einer Ordnerstruktur ===
CSS- und JavaScript-Dateien geladen werden. Allerdings gibt es dabei ein Problem: Die CSS- und JavaScript-Dateien werden im Laufe der
Zeit immer zahlreicher und/oder größer. Diese Dateien zu laden, dauert seine Zeit.


In der aktuellen Version der Web-Anwendung stehen diese Verweise auf diese Dateien im <code>head</code>-Bereich des Dokuments.
Grundsätzlich gilt auch bei der Programmierung: Ordnung ist das halbe Leben.
Dieser wird vollständig geladen, bevor der <code>body</code>-Bereich eingelesen wird. Das heißt aber, dass der Browser keine Inhalte des HTML-Dokuments
darstellen kann, solange er die JavaScript- und CSS-Dateien lädt. Wenn dies zu lange dauert, verliert der Besucher die Geduld und verläßt die Seite
vorzeitig.


Besser wäre es daher andersherum vorzugehen: Es wird zuerst der <code>body</code>-Bereich geladen und dann die JavaScript- und die CSS-Dateien.
Web-Anwendungen werden sehr schnell sehr groß. Also sollte man sich eine geeignete Ordnerstruktur überlegen.
Im Falle von JavaScript ist das durchaus sinnvoll, aber im Falle von CSS hat das den Effekt, dass der Browser keine Layout-Vorgaben erhalten hat,
Üblicherweise legt man CSS-Dateien in einen Ordner namens  „<code>css</code>und JavaScript-Dateien in einen Ordner namens
wenn er mit dem [[Rendern]] der Seite beginnt. Also verwendet er die browserspezifischen Defaultwerte. Das heißt, die Seite sieht zunächst
„<code>js</code>“ oder  „<code>lib</code>“. Sollten es viele CSS- und/oder JavaScript-Dateien werden,  
ganz anders aus, als vom Designer geplant. Wenn dann die CSS-Dateien geladen wurden, wird die Seite erneut gerendert und verändert ihr Aussehen.
legt man im entsprechenden Ordner geeignete Unterordner an.
Auch das ist verwirrend und wirkt unprofessionell.


Was also machen?
Legen Sie im <code>web</code>-Verzeichnis Ihres Projekts folgende Ordner an:
* <code>web/css</code>: Hier werden die eigentlichen CSS-Dateien der Web-Anwendung gespeichert, die von der App dynamisch nachgeladen werden sollen. In diesem Ordner könnten zur weiteren Strukturierung Unterordner angelegt werden. Allerdings ist das hier nicht notwendig. Es wird nur die Dateien „<code>head.css</code>“ und „<code>body.css</code>“ geben. '''Verschieben Sie die Datei <code>web/main.css</code> in diesen Ordner.'''
* <code>web/js</code>: Hierher kommen die JavaScript-Dateien, die von der Anwendung benötigt werden. '''Verschieben Sie die Datei <code>web/main.js</code> in diesen Ordner.'''


===CSS-Dateien===
Nehmen Sie in der Datei <code>web/index.html</code> folgende beiden Ersetzungen vor:
Für CSS-Dateien empfiehlt Google ernsthaft, die Link-Element ganz ans Ende der HTML-Datei zu stellen, also '''nach''' dem
* <code>main.css</code> <code>css/main.css</code>
schließenden <code>html</code>-Tag.
* <code>main.js</code> → <code>js/main.js</code>
Damit das Problem mit dem falschen Layout nicht auftritt, sollen wichtige CSS-Style-Befehle direkt – {{dh}} als CSS-Befehle innerhalb eines
<code>stxle</code>-Elements – in die HTML-Datei eingefügt
werden.<ref>{{Quelle|Google (Web)}}, Kapitel „CSS-Bereitstellung optimieren“, https://developers.google.com/speed/docs/insights/OptimizeCSSDelivery</ref>


Von beiden Vorschlägen rate ich dringend ab. Der erste Vorschlag ist nicht [[HTML]]-konform, ja noch nicht einmal [[SGML]]-konform. Es mag sein,
Wenn Sie jetzt die Datei <code>index.html</code> mit dem Live Server ausführen, sollte Ihre Anwendung wieder funktionieren.  
dass diverse Browser diese Syntax verstehen, korrekt ist sie trotzdem nicht. Der zweite Vorschlag hat zur Folge, dass Struktur (HTML) und Layout (CSS)
vermischt werden. Dies war einer der Kardinalfehler von frühen HTML-Versionen aus der Zeit, bevor es das CSS-Format gab. '''Tun Sie das nicht.'''


Gehen Sie so vor, wie es unter [[Multimedia-Programmierung: Best Practices#CSS|Multimedia-Programmierung: Best Practices]] beschrieben ist.
Sollte dies der Fall sein, so sollten Sie die Änderungen committen und evtl. auch gleich
per <code>git push</code> auf den Git-Server hochladen. '''Es schadet übrigens nie, vor einen Commit oder gar einen Push noch einmal zu überprüfen, ob die Anwendung noch läuft.'''


In unserer Anwendung haben wir bislang alles richtig gemacht, bis auf die fehlende automatische Komprimierung.
=== Modularisierung der Datei <code>main.js</code> ===
Diese wird weitrt unten beschrieben.


===JavaScript-Dateien===
Dateien wie die ECMAScript-Datei <code>main.js</code> aus dem dritten Teil des Tutoriums werden sehr schnell groß, wenn man alle Funktionalität in diese eine Datei einfügt. Dies reduziert die Lesbarkeit und die Wartbbarkeit des Codes.
[[Bertrand Meyer]], einer der wesentlichen Begründer der modularen und objektorientierten Programmierung, fordert,
dass ein Programmcode in sogenannte Module zerlegt werden sollen. Ein Modul stellt eine gewisse Funktionalität zur Verfügung. Die zugehörigen Funktionen werden exportiert und können von anderen Modulen importiert werden, sofern sie diese Funktionalität zur Erfüllung ihrer Aufgabe benötigen. Meyer hält es für sinnvoll, dass ein Modul jeweils nur eine oder sehr wenige Aufgaben erfüllt. Damit können Module von vielen anderen Modulen wiederverwendet werden.


Für JavaScript-Dateien empfiehlt Google ebenfalls, diese nicht schon zu Beginn zu laden.<ref>{{Quelle|Google (Web)}}, Kapitel „CSS-Bereitstellung optimieren“, https://developers.google.com/speed/docs/insights/BlockingJS</ref> Dieser Vorschlag ist sehr sinnvoll.
Die Datei <code>main.js</code> wird im Sinne von Meyer in zwei Teile aufgespalten:
<code>greet.js</code> und <code>main.js</code>. Die erste Datei enthält die eigentliche Begrüßungsfunktionalität,
die zweite initialisiert die App, {{dh}} aktiviert die Begrüßungsfunktiontionalität.  


Entfernen Sie das <code>script</code>-Element aus dem HTML-Header-Bereich und fügen Sie es vor dem schließenden <code>body</code>-Tag  ein:
Erstellen Sie die JavaScript-Datei <code>web/js/greet.js</code> und verschieben Sie die
beiden Funktionen <code>sayHello</code> und <code>sayHelloOnEnter</code>
(jeweils samt Funktionsrumpf :-) ) in diese Datei.


<source lang="html5">
Schreiben Sie vor jede der beiden Funktionsdefinitionen das Schlüsselwort <code>export</code>.
  ...
Mit dieser ES-6-Anweisung legen Sie fest, dass die beiden Funktionen von anderen
  <script type="text/javascript" src="../src/js/main.js"></script>
ES-6-Dateien mittels eines Import-Befehls importiert werden können. (Funktionen ohne
</body>
Export-Anweisung könnten von anderen ES-6-Dateien nicht importiert werden.)
</source>


===Node.js===
In der Datei <code>web/js/main.js</code> werden jetzt diese beiden Funktionen importiert.
Fügen Sie ganz am Anfang der Datei ({{dh}} vor der verbliebenen Init-Funktion)
die folgende Import-Anweisung ein:


Dieses Tutorium befasst sich zwar nicht mit der Erstellung von Node.js-Anwendungen, aber trotzdem benötigen wir
<source lang="javascript">
Node, da für die Komprimierung von Dateien und das automatische Erstellen von JavaScript-Dokumentationen
import * as greet from './greet.js';
derartiege Anwendungen zum Einsatz kommen.
</source>
 
Installieren Sie zunächst Node.js: <code>https://nodejs.org/en/</code>, Version 6.x. Dieverse Versionen finden Sie unter https://nodejs.org/en/download/.
Achten Sie darauf, dass die Option „<code>Add to PATH</code>“ ausgewählt wurde.
 
Starten Sie WebStorm neu und öffnen Sie das Terminal, um zu testen, ob <code>node</code> funktioniert:
 
* Klick auf Icon in der linken unteren Ecke → <code>Terminal</code>
* Geben Sie Folgendes ins Terminal ein: <code>node --version</code>


Wenn Node.js korrekt installiert und der Pfad mit den <code>node</code>-Binaries in die Systemvariable <code>PATH</code> eingetragen wurde,
Damit wird erreicht, dass alle (<code>*</code>) Elemente, die in der Datei <code>greet.js</code>
sollte die Versionsnummer von <code>node</code> ausgegeben werden.
exportiert werden, unter ihrem jeweiligen Namen im Objekt <code>greet</code> gespeichert werden.
Das heißt, innerhalb der Datei <code>main.js</code> gibt es nun die beiden Funktionen
<code>greet.sayHello</code> und <code>greet.sayHelloOnEnter</code>.


Sie werden feststellen, das Node.js zum Großteil über Konsolbefehle gesteuert wird.  
Fügen Sie jetzt noch das Attribut <code>type="module"</code> in das Script-Element in der
Zu Beginn mag das für eingefleischte [[Klickibunti]]-Fans ungewohnt sein, aber Sie werden sich schnell daran gewöhnen.
Datei <code>index.html</code> ein. Damit teilen Sie dem Browser mit, dass Sie die ECMAScript-6-Befehle <code>import</code> und <code>export</code> verwenden möchten.  


'''Tipp'''<br/>
Anderenfalls hätten sie Zugriff auf alle Objekte, Konstanten, Variablen und Funktionen einer geladenen Datei, so wie die bis ECMAScript 5 üblich war. Das heißt, das Laden einer Datei konnte zu massiven Problemen führen, wenn in zwei verschiedenen Dateien unterschiedliche Objekte etc. zufälligerweise gleich benannt wurden. Um diese Probleme zu umgehen gab es zahlreiche ziemlich aufwändige Hacks. Mit dem Modul-Konzept von ECMAScript gehören diese Probleme der Vergangenheit an.  
Profis verwenden unter Windows lieber ein Terminal, dass eine [[Unix]]-[[Shell]] simuliert.
(Unter Linux und Mac OS X ist das nicht notwendig. Dort läuft sowieso schon eine UNIX-Shell.)


Installieren Sie  „Git for Windows“ (https://git-for-windows.github.io/). In diesem Paket ist die „Git BASH“ enthalten.
Damit sollte Ihre Anwendung wieder funktionieren.
Das ist eine Terminal-Anwendung, die eine Linux-Bash emuliert und zahlreiche nützliche Befehle wie „<code>which</code>“, „<code>[[vi]]</code>“ ( :-) ) etc. zur
Verfügung stellt.


Öffnen Sie nun ein beliebiges Terminal (WebStorm-Terminal, Git Bash, Mac-OS-X-Terminal ...)
Vergessen Sie nicht, Ihr aktuelles Projekt zu committen und auf dem Git-Server zu speichern, sobald alles funktioniert.´
und geben Sie folgenden Befehl ein


<source lang="bash">
==== Musterlösung v04_1 ====
npm install -g bower grunt-cli jsdoc ink-docstrap yuglify node-minify
Verison v04_1 [https://glossar.hs-augsburg.de/beispiel/tutorium/2025/wk_hello_world/wk_hello_world_04/web/v04_1/index.html <code>index.html</code>] ([https://validator.nu/?doc=https%3A%2F%2Fglossar.hs-augsburg.de%2Fbeispiel%2Ftutorium%2F2025%2Fwk_hello_world%2Fwk_hello_world_04%2Fweb%2Fv04_1%2Findex.html HTML validate], [https://css-validator.org/validator?uri=https%3A%2F%2Fglossar.hs-augsburg.de%2Fbeispiel%2Ftutorium%2F2025%2Fwk_hello_world%2Fwk_hello_world_04%2Fweb%2Fv04_1%2Findex.html&profile=css21&usermedium=all&warning=1&lang=de CSS validate], [https://pagespeed.web.dev/analysis?url=https%3A%2F%2Fglossar.hs-augsburg.de%2Fbeispiel%2Ftutorium%2F2025%2Fwk_hello_world%2Fwk_hello_world_04%2Fweb%2Fv04_1%2Findex.html Google PageSpeed])
</source>


Damit werden die wichtigsten Node.js-Pakete ''gobal''  (Option „<code>-g</code>“) installiert, die wir im Folgenden noch benötigen.
===Laden der CSS- und der JavaScript-Dateien===
(OK, <code>bower</code> wird noch nicht benötigt. Das Paket ist aber so gut, dass wir es sicher noch brauchen werden.)
Die globale Installation hat zur Folge, dass jedes Projekt auf diese Pakete zugreifen kann, Pakete also nicht mehrfach installiert werden.


* Rechtsklick im Dateibaum auf  „<code>node-modules</code>“ → <code>Mark Directory as</code> → <code>excluded</code>
Führen Sie [https://pagespeed.web.dev/analysis?url=https%3A%2F%2Fglossar.hs-augsburg.de%2Fbeispiel%2Ftutorium%2F2025%2Fwk_hello_world%2Fwk_hello_world_04%2Fweb%2Fv04_1%2Findex.html Google PageSpeed] für die
* Rechtsklick im Dateibaum auf  „<code>doc</code>“ → <code>Mark Directory as</code> → <code>excluded</code>
Musterlösung der vorherigen Teilaufgabe aus und sehen Sie sich an, welche Verbesserungsvorschläge Google macht.


Beide Ordner enthalten Dateien, die nicht von Ihnen stammen. Diese Dateien können Fehler und Hinweise enthalten,
Folgendes findet Google PageSpeed verbesserungswürdig:
veraltete Schnittstellen (“deprecated”) verwenden etc. (Oh ja, auch andere Programmierer machen Fehler. :-) )
# [https://developer.chrome.com/docs/lighthouse/performance/render-blocking-resources?hl=de ''Ressourcen entfernen, die das Rendering blockieren.]''<br />''Mögliche Einsparung von 300 ms.''
Und wenn Sie diese beiden Orner nicht als „<code>excluded markieren</code>“, prüft WebStorm bei jedem Commit die
# [https://developer.chrome.com/docs/lighthouse/performance/critical-request-chains?hl=de ''Verkettung kritischer Anfragen vermeiden'']<br />''2 Ketten gefunden''
darin enthaltenen Dateien und weist Sie auf jedes Problem hin. Das können so vile Probleme sein, dass Sie die
# [''Largest Contentful Paint-Element 1.350 ms'' https://developer.chrome.com/docs/lighthouse/performance/lighthouse-largest-contentful-paint?hl=de]
Probleme in Ihren eigenen Datein vollkommen übersehen.
# ''Dokument enthält keine Meta-Beschreibung''


Nun müssen wir den Node-Paket-Manager „<code>npmn</code>“ (https://docs.npmjs.com/) sowie Grunt (http://gruntjs.com/) initialisieren.
''Anmerkung'': Google Pagespped ist auch über die Chroeme-Entwickler-Konsole zugänglich. Damit können auch lokale Server (<ocde>localhost</code> {{}}) getestet werden. Echte Prformanzprobleme erkennt man aber eher, wenn man seinen Live-Server testet.
Grunt ist '''der''' “JavaScript Task Runner”, also ein Programm, das Aufgaben (''tasks'') wie {{zB}} das Komprimieren von Programmdateien
durchführt.  


Die Initialisierung erfolgt durch die Bereitstellung von zei Dateien „<code>package.json</code>“
Eine Meta-Beschreibung hinzuzufügen, ist kein Problem. Fügen Sie folgende Zeile in den Head-Bereich der HTML-Datei ein.
und „<code>package.json</code>“ im Root-Verzeichnis der Web-Anwendung. Eine initilae Version der <code>package.json</code> könnten Sie theoretisch mit
<code>npm init</code> erstellen. Einfacher ist es jedoch, wenn Sie die beiden Datein von Hand erstellen und folgenden Code einfügen:


'''<code>package.json</code>''' (https://docs.npmjs.com/files/package.json)
<source language="HTML">
<source lang="bash">
<meta name="description"  
{
      content="A Hello World application that demonstrates best practices for HTML, CSS, and JavaScript files."
  "name": "hello_world_04",
>
  "version": "1.0.0",
  "description": "Hello World Tutorium",
  "main": "gruntfile.js",
  "directories": {
    "doc": "./doc"
  },
  "dependencies": {},
  "devDependencies": {
  },
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "repository": {
    "type": "svn",
    "url": "https://glossar.hs-augsburg.de/kowa/tutorium/es5/hello_world/WK_HelloWorld04"
  },
  "author": "Wolfgang Kowarschick",
  "license": "CC-BY-NC-SA-4.0"
}
</source>
</source>


Anmerkung: Diese Datei wird durch <code>npm</code> automatisch erstellt und geändert. Daher ist das Layout (welches mir nicht gefällt) vorgegeben.
Das effiziente Laden der JavaScript- und CSS-Dateien ist viel komplexer, aber auch viel wichtiger, da die CSS- und JavaScript-Dateien werden im Laufe der
Zeit immer zahlreicher und/oder größer werden. Diese Dateien zu laden, dauert seine Zeit. Auch das Laden von vielen kleinen Dateien dauert deutlich länger, als das Laden einer einzigen großen Datei. Das aber widerspricht der Modularisierung, die wir vorher vorgenommen haben. Modularisierung und Performance widersprechen sich zunächst einmal.


Hier sollten Sie natürlich nicht meinen Repositorypfad und meinen Namen einfügen, sondern jeweils Ihren. Sie können außerdem die
In Web-Anwendung aus dem dritten Teil des Tutoriums stehen die Verweise auf die CSS- und JavaScript-Dateien im <code>head</code>-Bereich des Dokuments.
Lizenz anpassen (https://spdx.org/licenses/), unter die Sie Ihre Software stellen, wenn Sie das möchten.
Die CSS-Datei wird vollständig geladen, bevor der <code>body</code>-Bereich eingelesen wird. Die JavaScript-Datei <code>main.js</code> wird dagegen parallel zum Body-Bereich geladen. Dies wird durch die Angabe von <code>async</code> im
Script-Tag wird erreicht.  


Die meisten Attribute sind selbsterklärend. Wichtig sind für uns vor allem zwei Attribute: „<code>main</code>“ und
Das heißt aber, dass der Browser keine Inhalte des HTML-Dokuments
„<code>devDependencies</code>“. Das erste enthält den Namen der Konfigurationsdatei von Grunt. Diese fehlt noch. Also: Legen Sie sie an
darstellen kann, solange er die CSS-Datei (und evtl. weitere Dateien wie Fonts, Hintergrundbilder etc.) lädt. Wenn dies zu lange dauert,
(und vergessen Sie nicht, Ihren Namen anstelle meines Namens einzufügen):
verliert der Besucher die Geduld und verlässt die Seite vorzeitig.


'''<code>gruntfile.js</code>'''
Besser wäre es daher andersherum vorzugehen: Es wird zuerst der Body-Bereich geladen und dann die JavaScript- und die CSS-Dateien.
<source lang="javascript">
Im Falle von JavaScript ist das durchaus sinnvoll (und durch die Angabe von <code>async</code> auch machbar), aber im Falle von CSS hat das den Effekt, dass der Browser noch keine Layout-Vorgaben erhalten hat,
/**
wenn er mit dem [[Rendern]] der Seite beginnt. Also verwendet er die browserspezifischen Defaultwerte. Das heißt, die Seite sieht zunächst
* @author Wolfgang Kowarschick <kowa@hs-augsburg.de>
ganz anders aus, als vom Designer geplant. Wenn dann die CSS-Dateien geladen wurden, wird die Seite erneut gerendert und verändert ihr Aussehen.
* @see http://gruntjs.com/getting-started
*
* Created on 2016/09/28.
*/


module.exports = function(grunt)
Auch das ist verwirrend und wirkt unprofessionell: https://de.wikipedia.org/wiki/Ampel
{
  // Project configuration.
  grunt.initConfig
  ({
    pkg: grunt.file.readJSON('package.json'),
  });


  // Load the plugin that provides the "uglify" task.
Was also machen?


  // Default task(s).
===CSS-Dateien===
};
Damit das Problem mit dem falschen Layout nicht auftritt, empfiehlt Google ([https://developers.google.com/speed/pagespeed/insights/ PageSpeed Insights]),
</source>
ganz wenige, wichtige CSS-Befehle direkt – {{dh}} als CSS-Befehle innerhalb eines <code>style</code>-Elements – in dem HTML-Head-Bereich einzufügen. Alle anderen CSS-Anweisungen sollen erst am Ende der HTML-Datei dynamisch mittels JavaScript
(oder mittels des PageSpeed-Optimization-Modul von Google) gelesen werden.


Diese Datei ist noch ziemlich leer, aber das wird sich ändern.
[https://pagespeed.web.dev/analysis?url=https%3A%2F%2Fglossar.hs-augsburg.de%2Fbeispiel%2Ftutorium%2F2025%2Fwk_hello_world%2Fwk_hello_world_04%2Fweb%2Fv04_1%2Findex.html PageSpeed Insights (für Musterlösung v04_1)]:
<blockquote cite="https://pagespeed.web.dev/analysis?url=https%3A%2F%2Fglossar.hs-augsburg.de%2Fbeispiel%2Ftutorium%2F2025%2Fwk_hello_world%2Fwk_hello_world_04%2Fweb%2Fv04_1%2Findex.html">
[https://developer.chrome.com/docs/lighthouse/performance/render-blocking-resources?hl=de Ressourcen blockieren den First Paint deiner Seite. Versuche, wichtiges JS und wichtige CSS inline anzugeben und alle nicht kritischen JS und Stile zurückzustellen.]
</blockquote>


Spannender ist in der Datei „<code>package.json</code>“ das Attribut „<code>devDependencies</code>“ (dev = developer).
Gehen Sie so vor, wie es Google empfohlen hat. Genauer gesagt, gehen Sie so vor,
Diese enthält eine Liste von von NodeJS-Paketen, die für die Entwicklung der Web-Anwendung benötigt werden.
wie es unter [[Multimedia-Programmierung: Best Practices#CSS|Multimedia-Programmierung: Best Practices]] beschrieben ist. In diesem und den folgenden Tutorien werden
Fügen wir einmal das Paket „<code>ink-docstrap</code>“ in diese Liste ein.
die dort beschriebenen Prinzipien berücksichtigt.
(Diese Pakte wird nachfolgend für die Erstellung der JavaScript-Dokumentation mittels JSDoc verwendet. Es enthält ein Theme, welches deutlich schöner ist als das Standard-JSDoc-Theme.)


Führen Sie folgenden Befehl im WebStorm-Terminal (oder mittels eine beliebigen anderen Shell im Wurzelverzeichnis Ihrer Web-Anwendung) aus:
Legen Sie die Datei <code>src/css/head.css</code> an und fügen Sie folgenden (sehr kurzen!) Code ein:


<source lang="bash">
<source lang="css">
npm install ink-docstrap --save-dev
body
</source>
{ background-color: #C5EFFC; }


Dies bewirkt zweierlei:
.hidden
 
{ display: none; }
# Das Node.js-Paket „<code> ink-docstrap</code>“ sowie diverse weitere Node.js-Pakete, die diese Paket benötigt, werden einem Projektordner namens „<code>node_modules</code>“ abgelegt.  
# Das Name und die Versionnummer des Paketes werden in das Attribut „<code>devDependencies</code>“ in der  Datei „<code>package.json</code>“ eingefügt (wegen der Option „<code>--save-dev</code>“).
 
Nun steht Ihnen der Befehl
<source lang="bash">
npm update
</source>
</source>
zur Verfügung. Mit diesem können Sie die lokal gepeicherte Versionen der im Attribut „<code>devDependencies</code>“ aufgeführte Node.js-Pakete
jederzeit auf die neueste Version aktualisieren. Sie önnen ein Paket auch löschen und dann mittels <code>npm update</code> (https://docs.npmjs.com/cli/update) wieder herstellen.
Versuchen Sie es:


<source lang="bash">
In der Datei <code>src/index.html</code> müssen Sie das Link-Element in Header folgendermaßen
npm remove ink-docstrap
<source lang="html">
npm list
<link rel="stylesheet" href="css/head.css"/>
</source>
</source>
anpassen. Das heißt, Sie laden im HTML-Head-Bereich nur noch die kleine CSS-Datei.
Damit funktioniert Ihre Web-Anwendung derzeit nur noch rudimentär: Es gibt kaum noch CSS-Anweisungen, die berücksichtigt werden. Noch besser ist es gemäß den Google-Empfehlungen,
wenn man die CSS-Regeln direkt in die HTML-Datei einfügt. Ersetzen Sie also das
Link-Element durch folgendes Style-Element:


Jetzt ist der Ordner „<code>node_modules</code>“  wieder leer.
<source lang="html">
<style>
body
{ background-color: #C5EFFC; }


<source lang="bash">
.hidden
npm update
{ display: none; }
npm list
</style>
</source>
</source>


Und jetzt enthält der wieder „<code>ink-docstrap</code>“ sowie andere dafür notwendige Pakete.
In der Datei <code>src/css/main.css</code> können Sie nun das
<code>.hidden</code>-Element und
das Farbattribut <code>background-color: #C5EFFC;</code> entfernen.


Sie können auch überprüfen, ob Sie in Ihrem Projekt irgendwelche veralteten Pakete benutzen (https://docs.npmjs.com/cli/outdated):
Es ist sinnvoll, die Datei <code>main.css</code> in <code>body.css</code> umzubenennen:
<source lang="bash">
<code>head.css</code> wird im HTML-Head-Bereich vor dem Body-Bereich geladen,
npm outdated
<code>body.css</code> soll parallel zum Body-Bereich geladen werden und dann zur Verfügung stehen, wenn der Body vollständig geladen wurde.
</source>


Kein Ergebnis ist hier ein gutes Ergebnis!
Leider ist CSS-Link-Element nicht möglich, <code>async</code> anzugeben, wie wir dies beim Skript-Element gemacht haben, um JavaScript-Code parallel zum Body-Element zu laden. Hier hilft derzeit nur ein kleiner Hack:


Und natürlich finktioniert das Ganze auch für global gespeicherte Pakete:
<source lang="html">
 
<!-- Load body.css asynchronously, i.e. parallel to the body content. -->
<source lang="bash">
<link rel="preload" href="css/body.css" as="style" onload="this.rel='stylesheet'">
npm -g outdated
npm -g list
</source>
</source>


Allerdings muss man veraltete globale Pakete erneut installieren. Zum Beipiel muss <code>npm</code>
Die Datei <code>body.css</code> wird für den Medientyp <code>style</code> parallel zum HTML-Dokument geladen, aber nicht aktiviert.
relativ häufig aktualisiert werden (sobald  „<code>npm -g outdated</code>“ das Paket als veraltet meldet):
Um aus dem Inhalt der vorab geladenen Datei für den Browser ein Stylesheet zu machen, muss man den Wert des Attributs <code>rel</code>
in <code>'stylesheet'</code> abändern. Das sollte erst erfolgen, wenn das Dokument vollständig geladen wurde, wenn also das
das Fensterobjekt des Browsers, in dem das HTML-Dokument gerendert werden soll, das Ereignis <code>load</code> feuert. Für dieses Ereignis kann man entweder im <code>link</code>-Tag den Händler <code>onload</code> einfügen oder man schreibt den Aktivierungsbefehl in die Init-Funktion in der Datei <code>main.js</code>.


<source lang="bash">
Für die zweite Variante sollte man dem <code>link</code>-Element eine eindeutigen Identifikator zuweisen:
npm install -g npm
<source lang="html">
<link id="body_css" rel="preload" href="css/body.css" as="style">
</source>
</source>


==Automatische Generierung der Schnittstellenbeschreibung==
Danach kann man den Aktivierungsbefehl in die Funktion <code>init</code> in der Datei <code>main.js</code> einfügen.
 
Installieren Sie zunächst JSDoc:
 
* Öffnen Sie das WebStorm-Terminal.
* <code>npm install -g jsdoc</code> (installiert JSDoc als Node.js-Anwendung; das haben Sie eigentlich schon gemacht, aber das macht nix :-) )
* <code>npm install ink-docstrap</code> (installiert ein JSDoc-Theme namens „<code>docstrap</code>“ ([https://docstrap.github.io/docstrap/]); 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  „<code>conf</code>“ eine JSON-Datei namens „<code>jsdoc.conf</code>.
Fügen Sie in diese Datei folgenden Code ein:


<source lang="javascript">
<source lang="javascript">
{
document.getElementById('body_css').rel = 'stylesheet';
  "tags":
</source>
  {
    "allowUnknownTags": true
  },
  "source":
  {
    "include":        ["./src/js"],
    "includePattern": ".+\\.js$",
    "excludePattern": "doc"
  },
  "plugins":
  [],
  "templates":
  {
    "systemName":      "WK_HelloWorld04",
    "footer":          "Created by Wolfgang Kowarschick",
    "copyright":      "2016",
    "navType":        "vertical",
    "theme":          "spacelab",
    "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
  }
}
</source>  


Ersetzen Sie im Attribut „<code>footer</code>“ meinen Namen durch Ihren, denn es ist Ihr Projekt, nicht meines. :-)
Zusammenfassung:
* In <code>head.css</code> stehen möglichst wenig CSS-Anweisungen. Sie sollten ausreichen, um die aktuelle Seite “above the fold” („über dem Zeitungsknick“) fehlerfrei zu rendern. Das heißt, es sollte alles korrekt gerendert werden, was nach dem Laden im größtmöglichen Browserfenster sichtbar ist.
* Der Inhalt der <code>head.css</code> sollte direkt in den Head-Bereich der HTML-Seite eingefügt werden.
* Die restlichen CSS-Anweisungen werden in die Datei <code>body.css</code> eingetragen.
* Diese Datei wird mittels eines kleinen Hacks asynchron, {{dh}} parallel zum Body-Element geladen.


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


Weitere Konfigurationsparameter werden unter http://usejsdoc.org/about-configuring-jsdoc.html beschrieben.
Da die Datei <code>head.css</code> nur die Hintergrundfarbe sowie die <code>.hidden</code>-Regel enthält, sollten alle HTML-Elemente, die gerendert werden sollen, zunächst unsichtbar gemacht werden. Bei einer Web-Anwendung, wie einem Spiel, die zum Laden länger braucht, könnte man eine Sanduhr oder einen Ladebalken einfügen, um anzuzeigen, dass man noch etwas warten muss. Üblicherweise, sollte man dafür sorgen,
dass das Laden einer Seite so schnell geht, dass der Benutzer gar nicht erst ungeduldig wird.


Sie können nun eine Dokumentation für Ihr Projekt erstellen:
Fügen Sie die CSS-Klasse <code>hidden</code> in beide HTML-Sektions ein:


* Öffnen Sie das WebStorm-Terminal.
<source lang="html">
* <code>jsdoc -c conf/jsdoc.json</code>
<section id="section_form" class="hidden">
...
</section>
<section id="section_hello" class="hidden">
...
</section>
</source>


Nach kurzer Zeit sollte der Ordner „<code>doc/jsdoc</code>“ erstellt und mit Inhalten gefüllt worden sein.
Wenn man jetzt das Dokument im Browser öffnet, sieht man nichts, außer einem hellblauen Hintergrund.  
Sehen Sie sich die Datei „<code>doc/jsdoc/index.html</code>“ im Browser an. Diese enthält nicht viel,
außer einem Drop-Daown-Menü „<code>global</code>“, in dem die drei von Ihnen erstellen globalen Funktionen
„<code>greet</code>“, „<code>greet_on_enter</code>“ und „<code>init</code>“ enthalten sind.
Klicken Sie auf eine dieser Funktionen.


===Globale Speicherung der JSDoc-Templates===
Abhilfe schafft hier ein kleiner JavaScript-Befehl, der <code>hidden</code> von der Eingabe-Section <code>hidden-form</code> entfernt, sobald das Dokument vollständig geladen wurde (samt allen CSS- und JavaScript-Dateien</code>):


Wenn Sie  „<code>ink-docstrap</code>“ nicht mittels
<source lang="javascript">
<source lang="bash">npm install ink-docstrap</source>
document.getElementById('section_form').classList.remove('hidden');
sondern global mittels
<source lang="bash">npm install -g ink-docstrap</source>
installieren (so wie Sie das auch schon bereits getan haben), werden die Template-Dateien nicht im Projektordner, sondern im globalen <code>npm</code>-Ordner abgelegt.
Das hat den Vorteil, dass Sie dieses Template für viele Projekte einsetzen können und es nicht bei jedem Projekt separat speichern müssen.
Der Nachteil ist, dass in der Datei „<code>conf/jsdoc.json</code>“ den absoluten Pfad des globalen Ordners angeben müssen.
Dieser Pfad sieht auf jedem Rechner anders aus. Bei mir lautet er beispielsweise:
<source lang="bash">c:/Users/kowa/AppData/Roaming/npm/node_modules/ink-docstrap/template</source>
 
Das heißt, wenn man im Team an einem Projekt arbeitet, müsste man für jedes Teammitglied eine eigene
JSDoc-Konfigurationsdatei anlegen, was nicht wirklich schön, aber doch machbar ist.
 
In der [https://glossar.hs-augsburg.de/beispiel/tutorium/es5/hello_world/WK_HelloWorld04/ Musterlösung] verwende ich die lokale Version „<code>docstrap</code>“
(es gibt allerdings auch eine Datei namens  „<code>conf/jsdoc_wk.json</code>“, die speziell für meinen Rechner erstellt wurde).
Die lokale Version von „<code>docstrap</code>“ ist alledering nicht im Repository enthalten. Nachdem Sie die Musterlösung auf Ihren Rechner kopiert haben
(vergessen, wie's geht? → [[HTML5-Tutorium:_JavaScript:_Hello_World_01#Projekte_aus_einem_Subversion-Repository_laden|HTML5-Tutorium: JavaScript: Hello World 04]]),
müssen Sie im WebStorm-Terminal der Musterlösung noch folgenden Befehl ausführe:
 
<source lang="bash">
npm update
</source>
</source>


Und schon sollten Sie die Dokumentation wie gewohnt erstellen können.
An welche Stelle dieser Befehl eingefügt wird, erfahren Sie im nachfolgenden Abschnitt.


Sobald Sie „<code>grunt</code>“ konfiguriert haben, sollte „<code>conf/jsdoc.json</code>“  auch in Ihrem Projekt fehlende Node.js-PAkete nachinstallieren.
===JavaScript-Dateien===


===Erstellung der JSDoc-Dokumentation mittels Klickbunti===
Für JavaScript-Dateien empfiehlt Google ebenfalls, diese nicht schon zu Beginn zu laden, sondern das Laden „aufzuschieben“. Das machen wir bereits mit Hilfe des Async-Attributs.


Falls Sie die Dokumentation icht immer über das Terminal erstellen möchten, können Sie
Fügen Sie nun den zuvor erwähnten Befehl
in WebStorm einen neuen Menüeintrag erstellen, über den Sie die Erzeugung der HTML-Dokumentation starten können.
<source lang="javascript">
 
document.getElementById('section_form').classList.remove('hidden');
Sie müssen dazu den Pfad ermitteln, unter dem Ihre <code>jsdoc</code>-Datei zu finden ist.
Unter Unix (Maac OS X, Linux, FreeBSD ...) lautet der Befehl, den Sie in ein Terminal eingeben müssen:
<source lang="bash">
which jsdoc
</source>
</source>
Unter '''Windows''' funktioniert dieser Befehl nur in der '''Git BASH'''.
als letzen Befehl in den Rumpf der Funktion <code>init</code> in der Datei
Außerdem müssen Sie unter Windows den Bash-Pfad modifizieren, damit auch Windows etwas damit anfangen kann.
<code>web/js/main.js</code> ein.
Bei mir gibt dieser Befehl Folgendes aus:
<source lang="bash">
/c/Users/kowa/AppData/Roaming/npm/jsdoc
</source>
Um <code>jsdoc</code> in WebStorm aufrufen zu können, müssen Sie in diesem Pfad „<code>/c</code>“ durch
<code>c:</code>“ und  „<code>jsdoc</code>“ durch „<code>jsdoc.cmd</code>“ ersetzen.
(Mac-User brauchen nichts zu ändern. :-) )


Erstellen Sie nun den neuen WebStorm-Menüeintrag:
Wenn Sie jetzt die Web-App starten, sollte sie wieder funktionieren.


* <code>File</code> → <code>Settings</code> → <code>Tools</code> → <code>Extenal Tools</code>
Was haben Sie erreicht? Solange die Anwendung geladen wird, sehen Sie nur einen blauen Hintergrund ohne Inhalt, bzw. {{iAllg}} einen so genannten Splash Screen. Wenn dann alles, {{dh}} alle CSS- und JavaScript-Dateien geladen wurden, wird die eigentliche Anwendung sichtbar.  
* Klick auf das Plus-Icon
** <code>Name</code>: <code>JSDoc</code>
** <code>Group</code>: <code>Documentation</code>
** <code>Description</code>: <code>Generates JavaScript documentation</code>
** Hacken bei allen Optionen (<code>Options</code>)
** Hacken bei allen Show-in-Optionen (<code>Show i</code>) mit ausnahme von „<code>Search results</code>“
* <code>Name</code>: <code>c:/Users/kowa/AppData/Roaming/npm/jsdoc.cmd</code> (hier kommt Ihr zuvor ermittelter Pfad hinein)
* <code>Name</code>: <code>-c $ProjectFileDir$/conf/jsdoc.json</code>
* <code>Name</code>: <code>$ProjectFileDir$</code>
* <code>OK</code>
* <code>OK</code>


Nun finden Sie an mehreren Stellen in WebStorm einen neuens Menüpunkt  <code>Documentation</code> mit einem Unterpunkt <code>JSDoc</code>:
Vergessen Sie nicht: Commit!


* Hauptmenü : <code>Tools</code> → <code>Documentation</code> → <code>JSDoc</code>
== Verbesserung der Init-Funktion ==
* Rechtsklick auf Datei im Projekt-Dateibaum → <code>Documentation</code> → <code>JSDoc</code>
* Rechtsklick auf Datei im Editor-Tabmenü → <code>Documentation</code> → <code>JSDoc</code>


Wenn Sie auf einen dieser Unterpunkte  „<code>JSDoc</code>“ klicken, erhalten Sie entweder eine Fehlermeldung ( :-( ) oder es wird eine neue Dokumentation erstellt
Bislang sieht die Init-Funktion folgendermaßen aus.
({{dh}}, Sie haben alles richtig gemacht :-) ).


Um sicher zu gehen, können Sie versuchsweise den Ordner „<code>doc</code>“ gemeinsam mit seinem gesamten Inhalt löschen und
<source lang="javascript">
<code>JSDoc</code> nochmals aufrufen.
function init()
{ document.getElementById('button_submit')
          .addEventListener('click', greet.sayHello);
  window.addEventListener('keydown', greet.sayHelloOnEnter);


===Sicherung im Repository===
  document.getElementById('body_css').rel = 'stylesheet';


Bevor Sie weitermachen: Haben Sie Ihre aktuelle Veriosn von <code>Hello World 04</code> schon im Repository gesichert?
  document.getElementById('section_form').classList.remove('hidden');
 
}
Sie sollten den „<code>doc</code>“ (im Gegensatz zum Ordner „<code>node_modules</code>“) zu Ihrem
Repository hinzufügen:
 
* Klick auf „<code>doc</code>“ im Dateibaum → <code>Subversion</code> → <code>Add to VCS</code>
 
==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 Variablenbezeichenern 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 Volumenbegrernzung
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 „<code>cssmin</code>“ für CSS-Dateien (https://www.npmjs.com/package/cssmin) und
„<code>UglifyJS2</code>“ für JavaScript-Dateien (https://github.com/mishoo/UglifyJS2). Gesteuert werden diese  “minimizer”
oder  “minifier” mittels dem bereits installierten ''JavaScript Task Runner'' <code>grunt</code>“.
 
Die für das Minimieren mittels Grunt notwendige Pakete wurden ebenfalls bereits installiert:
 
<source lang="bash">
npm install grunt grunt-contrib-watch grunt-contrib-cssmin grunt-contrib-uglify --save-dev
</source>
</source>


Nun müssen die Aufgaben zu Komprimieren der JavaScript- und CSS-Dateien in die Konfigurationsdatei
Man kann auch einen anderen Import-Befehl verwenden, um auf das Objekt <code>greet</code> verzichten zu können.
eingefügt werden:
 
'''<code>gruntfile.js</code>'''
<source lang="javascript">
<source lang="javascript">
/**
import { sayHello, sayHelloOnEnter } from './greet.js'
  * @author Wolfgang Kowarschick <kowa@hs-augsburg.de>
   
* @see http://gruntjs.com/getting-started
function init()
*
{ document.getElementById('button_submit')
* Created on 2016/09/28.
          .addEventListener('click', sayHello);
*/
  window.addEventListener('keydown', sayHelloOnEnter);


module.exports = function(grunt)
  document.getElementById('body_css').rel = 'stylesheet';
{
  // Project configuration.
  grunt.initConfig
  ({
    pkg: grunt.file.readJSON('package.json'),


    cssmin:
   document.getElementById('section_form').classList.remove('hidden');
    { options: /* https://github.com/jakubpawlowicz/clean-css */
}
      { roundingPrecision:   -1
      },
      minimize:
      { files:
        { 'web/css/min.main.css':  ['src/css/main.css'],
        }
      }
    },
   
    uglify:
    { options:
      { mangle: true
      },
      minimize:
      { files:
        { 'web/js/min.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']);
};
</source>
</source>


Die Funktion  „<code>grunt.initConfig</code>“ aufgerufen und es wird ihr ein [[JavaScript-Objekt]] übergeben.
Darüber hinaus kann man die Autofokus-Funktionalität verbessern. Das Autofokus-Attribut reagiert laut HTML-Standard nur in manchen Situation.
Im Attribut „<code>pkg</code>“ wird grund der Inhalt der Datei  „<code>package.json</code>“ übergeben, damit Grunt ermitteln kann, welche Pakete verfügbar sind.
Wenn man die Situationen  selbst definiert werden, in den Autofokus aktiv wird, muss man den Fokus per JavaSript setzen, sobald eine dieser Situationen eintritt.
Die beiden anderen Attribute „<code>cssmin</code>“ und „<code>unglify</code>“ (deren Namen frei wählbar sind), werden die beiden Minifier konfiguriert.
<source lang="javascript">
function autofocus()
{ document.getElementById('input_name').focus(); 
}


In beiden Fällen können Optionen angegeben werden, wie {{zB}}, dass CSS-Maßangaben nicht gerundet werden sollen (es gibt Designer, die bestehen
function reset()
beim Pixelmaß auf vier Nachkommastellen) oder dass geeignete JavaScript-Variablen-Namen „durch die Mangel gedreht“ (''mangle''), also durch kürze Namen ersetzt werden sollen.
{ document.getElementById('input_name').value = '';
Wichtiger ist jedoch die Anngabe, welche Dateien überhaupt erstellt werden und was für Source-Daten dafür verwendet werden solle.
  autofocus()
}


Zu jeder Datei, die erstellt wird, kann man eine ganze Liste von Sourcedateien angeben. Das hat zur Folge, dass alle  diese Soursedateien nacheinander
function init()
komprimiert und dann in die Zieldatei eingefügt werden. Auch das ist eine Optimierung, um das Laden einer Web-Anwendung zu beschleunigen.
{ // Say hello, if the submit button is clicked or the buttoen 'Enter' is pressed.
Eine große Datei wird per HTTP schneller übertragen als viele kleine. Künftig werden wir daher möglichst viele JavaScript-Dateien in komprimierter
  document.getElementById('button_submit').addEventListener('click', sayHello);
form in eine einige Datei namens „<code>min.js</code>“ einfügen.
  window.addEventListener('keydown', sayHelloOnEnter);


Nach der Initialisierungsfunktion werden noch zwei weitere Funktionen aufgerufen. Mit <code>grunt.loadNpmTasks</code>
  // Set the focus it the window is reentered.
werden die Grundplugins geladen, die die eigentliche Komprimierung vornehmen. Und mittels <code>grunt.loadNpmTasks</code>
  window.addEventListener('visibilitychange', autofocus);
wird festgelegt, welche Aufgaben standardmäiß ausgeführt werden, sobald der Befehl „<code>grunt</code>“ ausgeführt wird.
 
  // If the reset button is clicked, not only the textares are reset but also the focus is replaced.
  document.getElementById('button_reset').addEventListener('click', reset);


So, nun sollte alles funktionieren. Öffnen Sie das Web-Storm-Terminal und tipppen Sie nacheinander folgende Befehle ein:
  // Activate the body css file that had been preloaded asynchronously.
  document.getElementById('body_css').rel = 'stylesheet';


<source lang="bash">
  // Now that all files have been loaded, the app can be started.
grunt cssmin
  // So the form is made visible!
grunt uglify
  document.getElementById('section_form').classList.remove('hidden');
grunt
  autofocus();
<source>
}
</source>


Sie werden feststellen, das jedesmal bestimmte Dateien in den Ordnern „<code>web/css</code>“ oder/und „<code>web/js</code>“
==== Musterlösung v04_2 ====
erstellt werden, die mit dem Namensbestandteil „<code>min</code>“ beginnen. Sehen Sie sich die erzeugten Dateien an.
Verison v04_2 [https://glossar.hs-augsburg.de/beispiel/tutorium/2025/wk_hello_world/wk_hello_world_04/web/v04_2/index.html <code>index.html</code>] ([https://validator.nu/?doc=https%3A%2F%2Fglossar.hs-augsburg.de%2Fbeispiel%2Ftutorium%2F2025%2Fwk_hello_world%2Fwk_hello_world_04%2Fweb%2Fv04_2%2Findex.html HTML validate], [https://css-validator.org/validator?uri=https%3A%2F%2Fglossar.hs-augsburg.de%2Fbeispiel%2Ftutorium%2F2025%2Fwk_hello_world%2Fwk_hello_world_04%2Fweb%2Fv04_2%2Findex.html&profile=css21&usermedium=all&warning=1&lang=de CSS validate], [https://pagespeed.web.dev/analysis?url=https%3A%2F%2Fglossar.hs-augsburg.de%2Fbeispiel%2Ftutorium%2F2025%2Fwk_hello_world%2Fwk_hello_world_04%2Fweb%2Fv04_2%2Findex.html Google PageSpeed])
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 „<code>index.html</code>“ angepasst werden. Diese lädt noch die unkomprimierten Dateien aus dem
== Weitere Modularisierung ==
Source-Verzeichnis. Künftig soll sie die komprimierten Dateien aus dem eigentlichen Web-Ordner laden:


<source lang="html5">
Teilen Sie die Datei <code>greet.js</code> in zwei Dateien <code>greet.js</code> und <code>greet_on_enter.js</code> mit jeweils eines Funktion auf, um dem
...
[[Programmierprinzipien#Single_Responsibility_Principle.5B9.5D|Single Responsibility Principle]] zu genügen.
<link rel="stylesheet" type="text/css" href="css/min.main.css"/>
Versuchen Sie weitere Modularisierungsmöglichkeiten zu finden, um dem Single Responsibility Principle noch besser zu genügen.
...
<script type="text/javascript" src="js/min.js"></script>
</source>


Ab sofort werden nicht mehr die Source-Dateien geladen, sondern die komprimierten, durch die Minifier erstellten Dateien.
Musterlösung:<br/>
Das heißt aber für uns, dass wir bei jeder Änderung an den Source-Dateien den Befehl „<code>grunt</code>
v03: [https://glossar.hs-augsburg.de/beispiel/tutorium/2024/wk_hello_world/wk_hello_world_04/web/v04_3/index.html <code>index.html</code>], Git:
ausführen müssen, um die Aktualisierungen in die komprimierten Versionen der JavaScript bzw. CSS-Dateien zu übertragen.
[{{Git-Server}}/kowa/wk_hello_world_04/-/tree/master/web/v04_5?ref_type=heads v04_3]<br />
v04: [https://glossar.hs-augsburg.de/beispiel/tutorium/2024/wk_hello_world/wk_hello_world_04/web/v04_4/index.html <code>index.html</code>], Git:
[{{Git-Server}}/kowa/wk_hello_world_04/-/tree/master/web/v04_5?ref_type=heads v04_4]


Das ist lästig und geht besser.
== Konfigurierbarkeit ==


==Automatische Aktualisierung der komprimierten Dateien==
Entfernen Sie programmspezifische Konstanten aus den JavaScript-Dateien und definieren Sie diese in einer Datei <code>config.js</code> oder (sehr viel besser) <code>config.json</code>, um dem Prinzip der [[Programmierprinzipien#Konfigurierbarkeit.2C_Customizability|Konfigurierbarkeit]] zu genügen.


Mit Hilfe des NodeJS-Paketes „<code>grunt-contrib-watch</code>“ aknn man beliebige Dateibäume überwachen,
Musterlösung:<br/>
ob sich darin besgtimmte Dateien ändern und in einem derartigen Fall auomatisch andere Grunt Tasks ausführen.
v03: [https://glossar.hs-augsburg.de/beispiel/tutorium/2024/wk_hello_world/wk_hello_world_04/web/v04_5/index.html <code>index.html</code>], Git: [{{Git-Server}}/kowa/wk_hello_world_04/-/tree/master/web/v04_5?ref_type=heads v04_5]
Das kann man ausnutzen, um Änderungen bei einer CSS-Datei oder eine JavaScript-Datei die komprimierten
Dateien automatisch neu zu erstellen.


Öffnen Sie das Web-Stormterminal und laden Sie das besagte Paket:
Ich verwende hier die Funktionen <code>getJson</code> und <code>controlerKey</code> (bzw. die allgemeinere Variante <code>ControllerKey</code>, eine ECMAScript-Klasse), die erst später eingeführt und erklärt werden.


<source lang="bash">
Die Musterlösung müsste noch ausgebaut werden:
npm install grunt-contrib-watch --save-dev
* Für die Internationalisierung müsste eine interaktive Auswahlmöglichkeit geschaffen werden. Zurzeit kann der Sprachwechsel nur erfolgen, indem in <code>main.js</code> der Array-Zugriff <code>["de"]</code> durch <code>["en"]</code> ersetzt wird.
</source>  
* Für die Internationalisierung müssten eigene Module zur Verfügung gestellt werden.


Erweitern Sie nun die Datei „<code>gruntfile.js</code>“ um einen Watch-Task:
==Fortsetzung des Tutoriums==


<source lang="javascript">
Wenn man die Anwendung noch einmal mit Google testet, erhält man eine neue Empfehlung
watch:
{ css:
  { files:  ['src/css/**/*.css'],
    tasks:  ['cssmin']
  },
  js:
  { files:  ['src/js/**/*.js'],
    tasks:  ['uglify']
  },
  web:
  { files:  ['web/**/*'],
    options: { livereload: true,
              spawn:      false
            }
  }
}
</source>
 
Vergessen Sie nicht, das Plugin zu laden:
<source lang="javascript">
grunt.loadNpmTasks('grunt-contrib-watch');
</source>


Der Watch-Task überwacht drei verschiedene Ordner:
[https://pagespeed.web.dev/analysis?url=https%3A%2F%2Fglossar.hs-augsburg.de%2Fbeispiel%2Ftutorium%2F2025%2Fwk_hello_world%2Fwk_hello_world_04%2Fweb%2Fv04_2%2Findex.html], [https://pagespeed.web.dev/analysis?url=https%3A%2F%2Fglossar.hs-augsburg.de%2Fbeispiel%2Ftutorium%2F2025%2Fwk_hello_world%2Fwk_hello_world_04%2Fweb%2Fv04_3%2Findex.html], [https://pagespeed.web.dev/analysis?url=https%3A%2F%2Fglossar.hs-augsburg.de%2Fbeispiel%2Ftutorium%2F2025%2Fwk_hello_world%2Fwk_hello_world_04%2Fweb%2Fv04_4%2Findex.html], [https://pagespeed.web.dev/analysis?url=https%3A%2F%2Fglossar.hs-augsburg.de%2Fbeispiel%2Ftutorium%2F2025%2Fwk_hello_world%2Fwk_hello_world_04%2Fweb%2Fv04_5%2Findex.html]:
<blockquote cite="https://pagespeed.web.dev/analysis?url=https%3A%2F%2Fglossar.hs-augsburg.de%2Fbeispiel%2Ftutorium%2F2025%2Fwk_hello_world%2Fwk_hello_world_04%2Fweb_v01%2Fv01_1%2Findex.html">
* [https://developer.chrome.com/docs/lighthouse/performance/critical-request-chains?hl=de Verkettung kritischer Anfragen vermeiden] – 1/2/4/5 Ketten gefunden
* [https://developer.chrome.com/docs/lighthouse/performance/resource-summary?hl=de Halten Sie die Anfrageanzahl niedrig und die Übertragungsgröße gering] – 5 Anfragen • 5 KiB.
</blockquote>


* Wann immer sich eine CSS-Datei im Ordner „<code>src/css</code>“ oder einem darin enthaltenen Unterordner ändert, wird der Task  „<code>cssmin</code>“ ausgeführt.
''Anmerkung: Die aktuelle Version von PageSpeed Insights zeigt die zweite Empfehlung nicht mehr an, vermutlich weil das Hello-World-Beispiel so klein ist, dass die vorgeschlagene Verbesserung keine Vorteile mehr bringt. Wir beachten die ehemalige Empfehlung trotzdem.''
* Wann immer sich eineJavaScript-Datei im Ordner „<code>src/js</code>“ oder einem darin enthaltenen Unterordner ändert, wird der Task  „<code>uglify</code>“ ausgeführt.
* Wann immer sich irgendeine Datei im Web-Ordner ändert, wird eine <code>livereload</code>-Nachricht (http://livereload.com/) an einen lokalen Nodeserver 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 zuladen, wann immer sich etwas ändert.  
Das heißt, wir sollten die verschiedenen Dateien  
Dies erfolgt entweder mit speziellen Browser-Plugins (http://livereload.com/extensions/) oder mit einem kleinen JavaScript-Hack.
<code>main.js</code>, <code>greet.js</code> etc. wieder zu einer Datei zusammenfügen.
Und wir sollten alle Dateien komprimieren: Überflüssige Kommentare, Leerzeichen und Zeilenumbrüche
löschen, lange Variablen-, Konstanten- und Funktionsnamen verkürzen etc.


{{TBD}}
Dies machen wir im [[HTML5-Tutorium: JavaScript: Hello World 05|5. Teil des Tutoriums]].
Wenn man die Vorschläge von Google beachtet,
beschleunigt dies die Übertragung der Dateien vom Server zum Client, macht aber  den Code unlesbar.
Die Zusammenführung zweier Dateien widerspricht dem Prinzip der Modularisierung. Das Entfernen von Kommentaren,
Leerzeichen und Leerzeilen ist aus Entwickler-Sicht vollkommen kontraproduktiv.


<!-- „<code></code>“ npm install grunt-contrib-watch --save-dev
Daher hat es sich eingebürgert, modularen Code mit Kommentaren und Leerzeichen zu schreiben. Dieser wird dann, bevor er an einen Browser übergeben wird automatisch zusammengefasst und komprimiert (minifiziert).


-->
Sie sollten nun [[HTML5-Tutorium: JavaScript: Hello World 05|Teil 5 des Tutoriums]] bearbeiten.
Dort erfüllen wir die Google-Vorgaben mittels <code>vite</code>, einem mächtiges Werkzeug zur Umwandlung von Web-Dateien. Leider ist die Konfiguration dieses Werkzeugs sehr komplex.


==Quellen==
==Quellen==
<!--<references/>-->
<references/>
<ol>
<ol>
<li value="1"> {{Quelle|Kowarschick, W.: Multimedia-Programmierung}}</li>
<li value="1"> {{Quelle|Kowarschick, W.: Web-Programmierung}}</li>
</ol>
</ol>
<noinclude>[[Kategorie: HTML5-Tutorium: JavaScript: Hello World]][[Kategorie: HTML5-Beispiel]][[Kategorie:Kapitel:Multimedia-Programmierung:Beispiele]]</noinclude>
<noinclude>[[Kategorie: HTML5-Tutorium: JavaScript: Hello World]][[Kategorie: HTML5-Beispiel]][[Kategorie:Kapitel:Multimedia-Programmierung:Beispiele]]</noinclude>

Aktuelle Version vom 8. April 2025, 06:36 Uhr

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

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

Vorlesung WebProg

Inhalt | Teil 1 | Teil 2 | Teil 3 | Teil 4 | Teil 5 | Teil 6 | Vue 1 | Vue 2 | Vue 3 | Vue 4 | Vue 5 | Vue 6

Musterlösung
v01_1: index.html (HTML validate, CSS validate, Google PageSpeed)
v01_2: index.html (HTML validate, CSS validate, Google PageSpeed)
v01_3: index.html (HTML validate, CSS validate, Google PageSpeed)
v01_4: index.html (HTML validate, CSS validate, Google PageSpeed)
v01_5: index.html (HTML validate, CSS validate, Google PageSpeed)

Git-Repository

Anwendungsfälle (Use Cases)

Gegenüber dem dritten Teil des Tutoriums ändern sich die die Anwendungsfälle nicht. Die Anwendung leistet also genau dasselbe wie zuvor.

In diesem Teil des Tutoriums geht es darum, die Anwendung besser zu strukturieren, d. h. zu modularisieren (vgl. Programmierprinzipien).

Modularisierung

Eine Anwendung, wie z. B. ein HTML5-Spiel, besteht aus diversen unterschiedlichen Komponenten mit ganz unterschiedlichen Aufgaben. Bei einem Web-Frontend müssen beispielsweise verschieden Komponenten (Buttons, Scrolbars, Textfelder, Datumsfelder etc.) erstellt werden. Bei einem HTML5-Spiel müssen die Spielfiguren, die Spielszenen, die Spiellogik, die Benutzereingaben, die Darstellung das Spiels im Browser etc. erstellt und verwaltet werden, wobei das Erstellen und Verwalten teilweise während der Spielentwicklung und teilweise während der Spielausführung (Runtime) erfolgt.

An der Entwicklung eines Web-Systems oder eines Spiels sind üblicherweise mehrere oder gar viele Entwickler gleichzeitig beteiligt. Oft müssen einzelnen Komponenten an neue Endgeräte oder Betriebssysteme angepasst werden. Beispielsweise muss eine Tastatursteuerung durch eine Touch- oder Gestensteuerung ersetzt werden, wenn eine Anwendung so erweitert werden soll, dass es auch auf einem Smartphone oder Tablet läuft.

Das alles ist nur machbar, wenn man die Anwendung modularisiert, d. h. in kleine, möglichst unabhängige Komponenten unterteilt. Ein großer Feind der Modularisierung sind globale Variablen.

Je mehr Dateien von unterschiedlichen Autoren erstellt werden, desto größer ist die Gefahr, dass es beim Zugriff auf globale Variablen zu Namenskollisionen kommt. Außerdem weiß niemand, welche globale Größe noch benötigt wird und welche nicht. Sicherheitshalber löscht man daher keine dieser Variablen, auch wenn sie vollkommen veraltet sind.

Daher gilt der Grundsatz: Verwende so wenig globale Größen wie möglich.

Erstellen eines neuen Projekts

Erstellen Sie ein neues Projekt hello_world_04 als Fork von hello_world_03 (siehe Hello-World-02-Tutorium).

Die App

Erstellen einer Ordnerstruktur

Grundsätzlich gilt auch bei der Programmierung: Ordnung ist das halbe Leben.

Web-Anwendungen werden sehr schnell sehr groß. Also sollte man sich eine geeignete Ordnerstruktur überlegen. Üblicherweise legt man CSS-Dateien in einen Ordner namens „css“ und JavaScript-Dateien in einen Ordner namens „js“ oder „lib“. Sollten es viele CSS- und/oder JavaScript-Dateien werden, legt man im entsprechenden Ordner geeignete Unterordner an.

Legen Sie im web-Verzeichnis Ihres Projekts folgende Ordner an:

  • web/css: Hier werden die eigentlichen CSS-Dateien der Web-Anwendung gespeichert, die von der App dynamisch nachgeladen werden sollen. In diesem Ordner könnten zur weiteren Strukturierung Unterordner angelegt werden. Allerdings ist das hier nicht notwendig. Es wird nur die Dateien „head.css“ und „body.css“ geben. Verschieben Sie die Datei web/main.css in diesen Ordner.
  • web/js: Hierher kommen die JavaScript-Dateien, die von der Anwendung benötigt werden. Verschieben Sie die Datei web/main.js in diesen Ordner.

Nehmen Sie in der Datei web/index.html folgende beiden Ersetzungen vor:

  • main.csscss/main.css
  • main.jsjs/main.js

Wenn Sie jetzt die Datei index.html mit dem Live Server ausführen, sollte Ihre Anwendung wieder funktionieren.

Sollte dies der Fall sein, so sollten Sie die Änderungen committen und evtl. auch gleich per git push auf den Git-Server hochladen. Es schadet übrigens nie, vor einen Commit oder gar einen Push noch einmal zu überprüfen, ob die Anwendung noch läuft.

Modularisierung der Datei main.js

Dateien wie die ECMAScript-Datei main.js aus dem dritten Teil des Tutoriums werden sehr schnell groß, wenn man alle Funktionalität in diese eine Datei einfügt. Dies reduziert die Lesbarkeit und die Wartbbarkeit des Codes. Bertrand Meyer, einer der wesentlichen Begründer der modularen und objektorientierten Programmierung, fordert, dass ein Programmcode in sogenannte Module zerlegt werden sollen. Ein Modul stellt eine gewisse Funktionalität zur Verfügung. Die zugehörigen Funktionen werden exportiert und können von anderen Modulen importiert werden, sofern sie diese Funktionalität zur Erfüllung ihrer Aufgabe benötigen. Meyer hält es für sinnvoll, dass ein Modul jeweils nur eine oder sehr wenige Aufgaben erfüllt. Damit können Module von vielen anderen Modulen wiederverwendet werden.

Die Datei main.js wird im Sinne von Meyer in zwei Teile aufgespalten: greet.js und main.js. Die erste Datei enthält die eigentliche Begrüßungsfunktionalität, die zweite initialisiert die App, d. h. aktiviert die Begrüßungsfunktiontionalität.

Erstellen Sie die JavaScript-Datei web/js/greet.js und verschieben Sie die beiden Funktionen sayHello und sayHelloOnEnter (jeweils samt Funktionsrumpf :-) ) in diese Datei.

Schreiben Sie vor jede der beiden Funktionsdefinitionen das Schlüsselwort export. Mit dieser ES-6-Anweisung legen Sie fest, dass die beiden Funktionen von anderen ES-6-Dateien mittels eines Import-Befehls importiert werden können. (Funktionen ohne Export-Anweisung könnten von anderen ES-6-Dateien nicht importiert werden.)

In der Datei web/js/main.js werden jetzt diese beiden Funktionen importiert. Fügen Sie ganz am Anfang der Datei (d. h. vor der verbliebenen Init-Funktion) die folgende Import-Anweisung ein:

import * as greet from './greet.js';

Damit wird erreicht, dass alle (*) Elemente, die in der Datei greet.js exportiert werden, unter ihrem jeweiligen Namen im Objekt greet gespeichert werden. Das heißt, innerhalb der Datei main.js gibt es nun die beiden Funktionen greet.sayHello und greet.sayHelloOnEnter.

Fügen Sie jetzt noch das Attribut type="module" in das Script-Element in der Datei index.html ein. Damit teilen Sie dem Browser mit, dass Sie die ECMAScript-6-Befehle import und export verwenden möchten.

Anderenfalls hätten sie Zugriff auf alle Objekte, Konstanten, Variablen und Funktionen einer geladenen Datei, so wie die bis ECMAScript 5 üblich war. Das heißt, das Laden einer Datei konnte zu massiven Problemen führen, wenn in zwei verschiedenen Dateien unterschiedliche Objekte etc. zufälligerweise gleich benannt wurden. Um diese Probleme zu umgehen gab es zahlreiche ziemlich aufwändige Hacks. Mit dem Modul-Konzept von ECMAScript gehören diese Probleme der Vergangenheit an.

Damit sollte Ihre Anwendung wieder funktionieren.

Vergessen Sie nicht, Ihr aktuelles Projekt zu committen und auf dem Git-Server zu speichern, sobald alles funktioniert.´

Musterlösung v04_1

Verison v04_1 index.html (HTML validate, CSS validate, Google PageSpeed)

Laden der CSS- und der JavaScript-Dateien

Führen Sie Google PageSpeed für die Musterlösung der vorherigen Teilaufgabe aus und sehen Sie sich an, welche Verbesserungsvorschläge Google macht.

Folgendes findet Google PageSpeed verbesserungswürdig:

  1. Ressourcen entfernen, die das Rendering blockieren.
    Mögliche Einsparung von 300 ms.
  2. Verkettung kritischer Anfragen vermeiden
    2 Ketten gefunden
  3. [Largest Contentful Paint-Element 1.350 ms https://developer.chrome.com/docs/lighthouse/performance/lighthouse-largest-contentful-paint?hl=de]
  4. Dokument enthält keine Meta-Beschreibung

Anmerkung: Google Pagespped ist auch über die Chroeme-Entwickler-Konsole zugänglich. Damit können auch lokale Server (<ocde>localhost o. Ä.) getestet werden. Echte Prformanzprobleme erkennt man aber eher, wenn man seinen Live-Server testet.

Eine Meta-Beschreibung hinzuzufügen, ist kein Problem. Fügen Sie folgende Zeile in den Head-Bereich der HTML-Datei ein.

<meta name="description" 
      content="A Hello World application that demonstrates best practices for HTML, CSS, and JavaScript files."
>

Das effiziente Laden der JavaScript- und CSS-Dateien ist viel komplexer, aber auch viel wichtiger, da die CSS- und JavaScript-Dateien werden im Laufe der Zeit immer zahlreicher und/oder größer werden. Diese Dateien zu laden, dauert seine Zeit. Auch das Laden von vielen kleinen Dateien dauert deutlich länger, als das Laden einer einzigen großen Datei. Das aber widerspricht der Modularisierung, die wir vorher vorgenommen haben. Modularisierung und Performance widersprechen sich zunächst einmal.

In Web-Anwendung aus dem dritten Teil des Tutoriums stehen die Verweise auf die CSS- und JavaScript-Dateien im head-Bereich des Dokuments. Die CSS-Datei wird vollständig geladen, bevor der body-Bereich eingelesen wird. Die JavaScript-Datei main.js wird dagegen parallel zum Body-Bereich geladen. Dies wird durch die Angabe von async im Script-Tag wird erreicht.

Das heißt aber, dass der Browser keine Inhalte des HTML-Dokuments darstellen kann, solange er die CSS-Datei (und evtl. weitere Dateien wie Fonts, Hintergrundbilder etc.) lädt. Wenn dies zu lange dauert, verliert der Besucher die Geduld und verlässt die Seite vorzeitig.

Besser wäre es daher andersherum vorzugehen: Es wird zuerst der Body-Bereich geladen und dann die JavaScript- und die CSS-Dateien. Im Falle von JavaScript ist das durchaus sinnvoll (und durch die Angabe von async auch machbar), aber im Falle von CSS hat das den Effekt, dass der Browser noch keine Layout-Vorgaben erhalten hat, wenn er mit dem Rendern der Seite beginnt. Also verwendet er die browserspezifischen Defaultwerte. Das heißt, die Seite sieht zunächst ganz anders aus, als vom Designer geplant. Wenn dann die CSS-Dateien geladen wurden, wird die Seite erneut gerendert und verändert ihr Aussehen.

Auch das ist verwirrend und wirkt unprofessionell: https://de.wikipedia.org/wiki/Ampel

Was also machen?

CSS-Dateien

Damit das Problem mit dem falschen Layout nicht auftritt, empfiehlt Google (PageSpeed Insights), ganz wenige, wichtige CSS-Befehle direkt – d. h. als CSS-Befehle innerhalb eines style-Elements – in dem HTML-Head-Bereich einzufügen. Alle anderen CSS-Anweisungen sollen erst am Ende der HTML-Datei dynamisch mittels JavaScript (oder mittels des PageSpeed-Optimization-Modul von Google) gelesen werden.

PageSpeed Insights (für Musterlösung v04_1):

Ressourcen blockieren den First Paint deiner Seite. Versuche, wichtiges JS und wichtige CSS inline anzugeben und alle nicht kritischen JS und Stile zurückzustellen.

Gehen Sie so vor, wie es Google empfohlen hat. Genauer gesagt, gehen Sie so vor, wie es unter Multimedia-Programmierung: Best Practices beschrieben ist. In diesem und den folgenden Tutorien werden die dort beschriebenen Prinzipien berücksichtigt.

Legen Sie die Datei src/css/head.css an und fügen Sie folgenden (sehr kurzen!) Code ein:

body
{ background-color: #C5EFFC; }

.hidden
{ display: none; }

In der Datei src/index.html müssen Sie das Link-Element in Header folgendermaßen

<link rel="stylesheet" href="css/head.css"/>

anpassen. Das heißt, Sie laden im HTML-Head-Bereich nur noch die kleine CSS-Datei. Damit funktioniert Ihre Web-Anwendung derzeit nur noch rudimentär: Es gibt kaum noch CSS-Anweisungen, die berücksichtigt werden. Noch besser ist es gemäß den Google-Empfehlungen, wenn man die CSS-Regeln direkt in die HTML-Datei einfügt. Ersetzen Sie also das Link-Element durch folgendes Style-Element:

<style>
body
{ background-color: #C5EFFC; }

.hidden
{ display: none; }
</style>

In der Datei src/css/main.css können Sie nun das .hidden-Element und das Farbattribut background-color: #C5EFFC; entfernen.

Es ist sinnvoll, die Datei main.css in body.css umzubenennen: head.css wird im HTML-Head-Bereich vor dem Body-Bereich geladen, body.css soll parallel zum Body-Bereich geladen werden und dann zur Verfügung stehen, wenn der Body vollständig geladen wurde.

Leider ist CSS-Link-Element nicht möglich, async anzugeben, wie wir dies beim Skript-Element gemacht haben, um JavaScript-Code parallel zum Body-Element zu laden. Hier hilft derzeit nur ein kleiner Hack:

<!-- Load body.css asynchronously, i.e. parallel to the body content. -->
<link rel="preload" href="css/body.css" as="style" onload="this.rel='stylesheet'">

Die Datei body.css wird für den Medientyp style parallel zum HTML-Dokument geladen, aber nicht aktiviert. Um aus dem Inhalt der vorab geladenen Datei für den Browser ein Stylesheet zu machen, muss man den Wert des Attributs rel in 'stylesheet' abändern. Das sollte erst erfolgen, wenn das Dokument vollständig geladen wurde, wenn also das das Fensterobjekt des Browsers, in dem das HTML-Dokument gerendert werden soll, das Ereignis load feuert. Für dieses Ereignis kann man entweder im link-Tag den Händler onload einfügen oder man schreibt den Aktivierungsbefehl in die Init-Funktion in der Datei main.js.

Für die zweite Variante sollte man dem link-Element eine eindeutigen Identifikator zuweisen:

<link id="body_css" rel="preload" href="css/body.css" as="style">

Danach kann man den Aktivierungsbefehl in die Funktion init in der Datei main.js einfügen.

document.getElementById('body_css').rel = 'stylesheet';

Zusammenfassung:

  • In head.css stehen möglichst wenig CSS-Anweisungen. Sie sollten ausreichen, um die aktuelle Seite “above the fold” („über dem Zeitungsknick“) fehlerfrei zu rendern. Das heißt, es sollte alles korrekt gerendert werden, was nach dem Laden im größtmöglichen Browserfenster sichtbar ist.
  • Der Inhalt der head.css sollte direkt in den Head-Bereich der HTML-Seite eingefügt werden.
  • Die restlichen CSS-Anweisungen werden in die Datei body.css eingetragen.
  • Diese Datei wird mittels eines kleinen Hacks asynchron, d. h. parallel zum Body-Element geladen.

index.html

Da die Datei head.css nur die Hintergrundfarbe sowie die .hidden-Regel enthält, sollten alle HTML-Elemente, die gerendert werden sollen, zunächst unsichtbar gemacht werden. Bei einer Web-Anwendung, wie einem Spiel, die zum Laden länger braucht, könnte man eine Sanduhr oder einen Ladebalken einfügen, um anzuzeigen, dass man noch etwas warten muss. Üblicherweise, sollte man dafür sorgen, dass das Laden einer Seite so schnell geht, dass der Benutzer gar nicht erst ungeduldig wird.

Fügen Sie die CSS-Klasse hidden in beide HTML-Sektions ein:

<section id="section_form" class="hidden">
...
</section>
<section id="section_hello" class="hidden">
...
</section>

Wenn man jetzt das Dokument im Browser öffnet, sieht man nichts, außer einem hellblauen Hintergrund.

Abhilfe schafft hier ein kleiner JavaScript-Befehl, der hidden von der Eingabe-Section hidden-form entfernt, sobald das Dokument vollständig geladen wurde (samt allen CSS- und JavaScript-Dateien):

document.getElementById('section_form').classList.remove('hidden');

An welche Stelle dieser Befehl eingefügt wird, erfahren Sie im nachfolgenden Abschnitt.

JavaScript-Dateien

Für JavaScript-Dateien empfiehlt Google ebenfalls, diese nicht schon zu Beginn zu laden, sondern das Laden „aufzuschieben“. Das machen wir bereits mit Hilfe des Async-Attributs.

Fügen Sie nun den zuvor erwähnten Befehl

document.getElementById('section_form').classList.remove('hidden');

als letzen Befehl in den Rumpf der Funktion init in der Datei web/js/main.js ein.

Wenn Sie jetzt die Web-App starten, sollte sie wieder funktionieren.

Was haben Sie erreicht? Solange die Anwendung geladen wird, sehen Sie nur einen blauen Hintergrund ohne Inhalt, bzw. i. Allg. einen so genannten Splash Screen. Wenn dann alles, d. h. alle CSS- und JavaScript-Dateien geladen wurden, wird die eigentliche Anwendung sichtbar.

Vergessen Sie nicht: Commit!

Verbesserung der Init-Funktion

Bislang sieht die Init-Funktion folgendermaßen aus.

function init()
{ document.getElementById('button_submit')
          .addEventListener('click', greet.sayHello);
  window.addEventListener('keydown', greet.sayHelloOnEnter);

  document.getElementById('body_css').rel = 'stylesheet';

  document.getElementById('section_form').classList.remove('hidden');
}

Man kann auch einen anderen Import-Befehl verwenden, um auf das Objekt greet verzichten zu können.

import { sayHello, sayHelloOnEnter } from './greet.js'
 
function init()
{ document.getElementById('button_submit')
          .addEventListener('click', sayHello);
  window.addEventListener('keydown', sayHelloOnEnter);

  document.getElementById('body_css').rel = 'stylesheet';

  document.getElementById('section_form').classList.remove('hidden');
}

Darüber hinaus kann man die Autofokus-Funktionalität verbessern. Das Autofokus-Attribut reagiert laut HTML-Standard nur in manchen Situation. Wenn man die Situationen selbst definiert werden, in den Autofokus aktiv wird, muss man den Fokus per JavaSript setzen, sobald eine dieser Situationen eintritt.

function autofocus()
{ document.getElementById('input_name').focus();   
}

function reset()
{ document.getElementById('input_name').value = '';
  autofocus();  
}

function init()
{ // Say hello, if the submit button is clicked or the buttoen 'Enter' is pressed.
  document.getElementById('button_submit').addEventListener('click', sayHello);
  window.addEventListener('keydown', sayHelloOnEnter);

  // Set the focus it the window is reentered.
  window.addEventListener('visibilitychange', autofocus);
  
  // If the reset button is clicked, not only the textares are reset but also the focus is replaced.
  document.getElementById('button_reset').addEventListener('click', reset);

  // Activate the body css file that had been preloaded asynchronously.
  document.getElementById('body_css').rel = 'stylesheet';

  // Now that all files have been loaded, the app can be started.
  // So the form is made visible!
  document.getElementById('section_form').classList.remove('hidden');
  autofocus();
}

Musterlösung v04_2

Verison v04_2 index.html (HTML validate, CSS validate, Google PageSpeed)

Weitere Modularisierung

Teilen Sie die Datei greet.js in zwei Dateien greet.js und greet_on_enter.js mit jeweils eines Funktion auf, um dem Single Responsibility Principle zu genügen. Versuchen Sie weitere Modularisierungsmöglichkeiten zu finden, um dem Single Responsibility Principle noch besser zu genügen.

Musterlösung:
v03: index.html, Git: v04_3
v04: index.html, Git: v04_4

Konfigurierbarkeit

Entfernen Sie programmspezifische Konstanten aus den JavaScript-Dateien und definieren Sie diese in einer Datei config.js oder (sehr viel besser) config.json, um dem Prinzip der Konfigurierbarkeit zu genügen.

Musterlösung:
v03: index.html, Git: v04_5

Ich verwende hier die Funktionen getJson und controlerKey (bzw. die allgemeinere Variante ControllerKey, eine ECMAScript-Klasse), die erst später eingeführt und erklärt werden.

Die Musterlösung müsste noch ausgebaut werden:

  • Für die Internationalisierung müsste eine interaktive Auswahlmöglichkeit geschaffen werden. Zurzeit kann der Sprachwechsel nur erfolgen, indem in main.js der Array-Zugriff ["de"] durch ["en"] ersetzt wird.
  • Für die Internationalisierung müssten eigene Module zur Verfügung gestellt werden.

Fortsetzung des Tutoriums

Wenn man die Anwendung noch einmal mit Google testet, erhält man eine neue Empfehlung

[1], [2], [3], [4]:

Anmerkung: Die aktuelle Version von PageSpeed Insights zeigt die zweite Empfehlung nicht mehr an, vermutlich weil das Hello-World-Beispiel so klein ist, dass die vorgeschlagene Verbesserung keine Vorteile mehr bringt. Wir beachten die ehemalige Empfehlung trotzdem.

Das heißt, wir sollten die verschiedenen Dateien main.js, greet.js etc. wieder zu einer Datei zusammenfügen. Und wir sollten alle Dateien komprimieren: Überflüssige Kommentare, Leerzeichen und Zeilenumbrüche löschen, lange Variablen-, Konstanten- und Funktionsnamen verkürzen etc.

Dies machen wir im 5. Teil des Tutoriums. Wenn man die Vorschläge von Google beachtet, beschleunigt dies die Übertragung der Dateien vom Server zum Client, macht aber den Code unlesbar. Die Zusammenführung zweier Dateien widerspricht dem Prinzip der Modularisierung. Das Entfernen von Kommentaren, Leerzeichen und Leerzeilen ist aus Entwickler-Sicht vollkommen kontraproduktiv.

Daher hat es sich eingebürgert, modularen Code mit Kommentaren und Leerzeichen zu schreiben. Dieser wird dann, bevor er an einen Browser übergeben wird automatisch zusammengefasst und komprimiert (minifiziert).

Sie sollten nun Teil 5 des Tutoriums bearbeiten. Dort erfüllen wir die Google-Vorgaben mittels vite, einem mächtiges Werkzeug zur Umwandlung von Web-Dateien. Leider ist die Konfiguration dieses Werkzeugs sehr komplex.

Quellen

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