HTML5-Tutorium: JavaScript: Hello World 05
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) |
Inhalt | Teil 1 | Teil 2 | Teil 3 | Teil 4 | Teil 5 | Teil 6 | Vue 1 | Vue 2 | Vue 3 | Vue 4
Musterlösung
v02: index.html
(HTML validate, CSS validate, Google PageSpeed)
v03: index.html
(HTML validate, CSS validate, Google PageSpeed)
v04: index.html
(HTML validate, CSS validate, Google PageSpeed)
Anwendungsfälle (Use Cases)
Gegenüber dem dritten (und dem vierten) Teil des Tutoriums ändern sich die die Anwendungsfälle nicht. Die Anwendung leistet also genau dasselbe wie zuvor.
Wie wir im vorherigen Teil des Tutoriums gesehen haben, sollten wird 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.
Da dies gegen das Prinzip der Modularisierung spricht, wird dies nicht im Sourcecode, sondern automatisch mit Hilfe eines geeigneten Tools (Vite, webpack, ...) realisiert .
Das heißt, der Sourcecode wird weiterhin modular aufgebaut. Anschließend wird der Sourcecode mit Hilfe eines Transpilers (Source-to-Source-Compiler) in eine kompakte Darstellung transformiert. Die Anzahl der Dateien, die vom Browser geladen werden müssen, wird drastisch reduziert, überflüssige Leerzeichen und Kommentare werden entfernt, Variablennamen werden durch kurze Namen ersetzt etc.
Ein Transpiler kann auch die Programmiersprache ändern. Zum Beispiel kann ECMAScript 6 in ECMAScript 5 übersetzt werden. Oder man verwendet eine Sprache wie TypeScript, CoffeeScript etc. oder sogar Java und übersetzt diese in ECMAScript, damit der Browser den Code interpretieren kann.
In diesem Teil des Tutoriums geht es also darum, die Anwendung mittels Vite 5[1] automatisch für den Server-Betrieb zu optimieren, sodass möglichst wenige, möglichst kleine Dateien zum Client übertragen werden.
JavaScript Module Bundlers
Um Module einer Web-Anwendung für die Auslieferung zum Client zu komprimieren, zu bündeln und anderweitig zu optimieren, gibt es zahlreiche Werkzeuge. Für Node.js gibt es die sogenannten “JavaScript Module Bundlers”, wie z. B. Vite[1], Webpack[2] oder Browserify[3].
Üblicherweise optimieren die Bundlers nicht nur JavaScript-Dateien, sondern auch HTML-, CSS-, JSON- und andere Dateien.
Erstellen eines neuen Vite-Projekts (Version 00)
Erstellen Sie ein neues Vite-Projekt hello_world_05
:
cd /webprog # Ordner, in dem Ihre Projekte liegen
npm create vite@latest
Geben Sie für Vite folgende Optionen an:
Project name: hello_world_05
Select a framework: Vanilla
Select a variant: JavaScript
Öffnen Sie das Projekt in Visual Studio Code (VSC) und öffnen Sie in VSC ein zugehöriges Terminal (für den neuen Projektordner). Führen Sie folgende Befehle aus:
npm i
npm run dev
Nun wird ein Server gestartet und eine URL angezeigt, die Sie in einem Browser öffnen können. Make it so!
Stellen Sie anschließend das Projekt unter Git-Kontrolle und speichern Sie es wieder in Gitlab.
Zur Erinnerung: .gitignore
Achtung: Bevor Sie einen Commit durchführen, müssen Sie im selben Ordner, in dem sich die Datei package.json
befindet, eine Datei namens .gitignore
anlegen, in dem eine Zeile mit dem Wort node_modules
enthalten ist. (Der Punkt zu Beginn des Dateinamens darf nicht fehlen!)
Glücklicherweise hat dies das Vite-Setup für Sie übernommen.
Diese Datei ist extrem wichtig. Wie Sie bereits im Node.js-Tutorium gesehen haben, enthält der
Ordner node_modules
sehr schnell sehr viele Dateien (mehrere Zehntausend).
Wenn Sie diese im Repository speichern, bläht sich dieses extrem stark auf. Die Git-Push- und -Pull-Befehle werden dadurch sehr langsam.
Die Datei .gitignore
dient dazu, dies zu verhindern. In jeder Zeile steht ein Dateiname oder ein Dateipattern (wie zum Beispiel *.log
, das alle Dateien mit der Dateiextension log
beschreibt), um zu verhindern, dass die zugehörigen Dateien ins Git-Repository eingefügt werden.
Wenn Sie dafür sorgen, die Dateien package.json
und package-lock.json
ins Repository eingefügt werden, kann ein anderer Benutzer, der Ihr Git-Repository auf seinen Rechner lädt, die fehlenden Node.js-Module jederzeit ganz einfach restaurieren:
npm i
Konfigurieren des Vite-Projekts Version 01
Das neu angelegte Projekt enthält keine Vite-Konfigurationsdatei. Defaultmäßig werden alle Dateien des Web-Projekts im Root-Ordner hello_world_05
abgelegt. Das ist – wie Sie bereits wissen – schlecht, da dort auch andere Dateien liegen, wie z. B. package.json
oder node_modules
.
Dies können Sie ändern, indem Sie eine Konfigurationsdatei vite.config.js
im Projektordner anlegen:
// https://vitejs.dev/config/
export default
{ root: 'src',
publicDir: '../public',
base: '',
build:
{ outDir: '../web',
minify: true,
}
}
Diese legt Folgendes fest:
- Der Root-Ordner des Projekts befindet sich im Unterordner
src
des Projektordners. Dort hinein kommen die ganzen HTML-/CSS-/JS-Dateien und Medien, die unter Kontrolle von Vite stehen sollen. - Der Ordner
public
, in dem alle Dateien enthalten sind, die von Vite nicht verändert werden, befindet sich weiterhin im Unterordnerpublic
des Projektordners. Vom Projektordnersrc
aus gesehen, liegt er außerhalb vonsrc
eine Ebene höher. Das heißt, er befindet sich in der Ordnerhierarchie auf derselben Ebene wiesrc
selbst. - Die Option
base
enthält einen String, der vor jeden Asset-Pfad (.css
,.js
,.jpg
...) eingefügt wird. Standardmäßig lautet der Wert'/'
. Das heißt, die zugehörigen Assetpfade sind absolut. Sie beginnen stets beim Top-Level-Verzeichnis der Web-App. Mit der Optionbase: ''
legt man fest, dass die Pfade relativ sind. Das hat den Vorteil, dass man die Web-App in einen Unterordner einer anderen Web-App legen kann (Beispiel:Verion 00
funktioniert nicht, da absolute Pfade verwendet werden). - Wenn Sie das Projekt mittels
npm run build
für den Produktivbetrieb erstellen lassen, wird das Ergebnis in den Ordnerweb
geschrieben (Defaultmäßig heißt der Ordnendist
). Dieser Ordner liegt ebenfalls auf derselben Ebene wiesrc
. - Durch
npm run build
wird komprimierter (minimized) Code erzeugt. Das gilt allerdings nicht für HTML-Dateien, sondern nur für CSS-, JavaScript-/TypeScript- und SVG-Dateien.
Sie sollten in der Datei package.json
im Skript build
auch noch die Option --emptyOutDir
ergänzen,
damit der Web-Ordner vor jedem Rebuild geleert wird. Ohne diese Option sammeln sich im Web-Ordner im Laufe der Zeit Dateileichen an. Außerdem sollten Sie die Buildanweisung auch ausführen, bevor Sie sich das Ergebnis eines
Build-Laufs mittels npm run preview
ansehen.
Die erreichen Sie, indem Sie den Build-Befehl zusätzlich unter dem Namen prepreview
einfügen.
"build": "vite build --emptyOutDir",
"prepreview": "vite build --emptyOutDir",
Grundsätzlich gilt für die Datei package.json
: Wenn Sie npm run xyz
ausführen,
wird zuvor automatisch npm run prexyz
ausgeführt, sofern prexyz
definiert wurde. Nach
npm run xyz
automatisch npm run postxyz
ausgeführt, sofern postxyz
definiert wurde.
Restrukturierung des Projekts
Erstellen Sie folgende Ordnerstruktur:
src | css | img | js
Verschieben Sie anschließend die Web-Dateien in diese Ordner:
src | css - style.css | img - javascript.svg | js - counter.js - main.js - index.html
Das Ergebnis ist, dass Sie Fehlermeldungen erhalten, weil bestimmte Dateien nicht gefunden werden.
Das liegt daran, dass sich die Ordnerstruktur aus Sicht der Dateien im src
-Ordner geändert haben.
Die Pfade innerhalb der Dateien in diesem Ordner müssen daher angepasst werden.
Die Pfade zu Dateien im public
-Ordner bleiben gleich. Aus Sicht der Dateien im src
-Ordner
befinden sie sich in der Root des Web-Auftritts. Im public
-Ordner liegt in diesem Beispiel nur die Datei
vite.svg
. Deren Pfade ändern sich nicht, daher bleiben in den Dateien src/index.html
und src/js/main.js
die Zeilen
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
bzw.
import viteLogo from '/vite.svg'
unverändert. Die anderen Pfade müssen dagegen eventuell angepasst werden:
// src/index.html
<script type="module" src="/src/main.js"></script>
// src/js/main.js
import './style.css'
import javascriptLogo from './javascript.svg'
import { setupCounter } from './counter.js'
Nehmen Sie geeignete Änderungen vor (sofern sie von VSC nicht automatisch durchgeführt wurden).
Modifizieren und testen Sie die App mit Hilfe von npm run dev
solange, bis sie fehlerfrei läuft.
Packen der Web-Anwendung
Bislang zeigt sich der Vorteil von Vite noch nicht wirklich. Der einzige Vorteil ist, dass CSS-Dateien auch in JavaScript-Dateien importiert werden können. Das ist im HTML-Standard nicht vorgesehen.
Seine Stärke spielt Vite aus, wenn wir das Projekt packen. Das bedeutet, dass die Anzahl der Dateien reduziert wird und überflüssige Leerzeichen, Zeilenumbrüche, Kommentare etc. entfernt werden. Dadurch reduziert sich die Anzahl der Bytes, die zu einem Client (Browser) übertragen werden müssen, erheblich. Insbesondere für Smartphones mit einem geringen Datenvolumen profitieren davon.
Intelligente Frameworks wie Vue, Svelte oder React oder React gehen sogar noch einen Schritt weiter und laden nur die Teile einer Web-Seite (nicht Web-Site!) nach, die sich geändert haben. JavaScript-Bibliotheken werden so definiert, dass sie für eine ganze Web-Site nur einmal geladen werden müssen und für verschiedene Seiten wiederverwendet werden können. Hier spielen Web-Frameworks ihre Stärken aus. Sie stützen sich dabei auf Bundler wie Vite oder Webpack.
Führen Sie folgenden Befehl aus:
npm run build
Durch diesen Befehl wird die Web-App gepackt und im Ordner web
abgelegt.
Öffnen Sie die Datei web/index.html
mit Hilfe des Live-Server-Plugins und sehen Sie sich das Ergebnis an.
Schauen Sie sich auch die generierten Dateien im Ordner web
an.
Testen Sie die Web-App dann, indem Sie entweder web/index.html
mit dem Live Server von VSC starten
oder indem Sie npm run preview
eingeben.
Hello-World-App (Version 02)
Ersetzen Sie nun die Vite-Default-App durch die Hello-World-App aus dem 4. Teil des Tutoriums. In der Musterlösung habe ich die Version 04_4 als Hello-World-App verwendet.
Im Public-Bereich der Anwendung liegt noch die Datei vite.svg
. Diese brauchen wir nicht mehr. Sie kann gelöscht werden. Dafür können Sie Dateien wie robots.txt
oder favicon.ico einfügen.
robots.txt
enthält Hinweise für die Suchmaschinen-Crawler, welche Dateien indexiert werden sollen und welche nicht. Die Crawler müssen sich aber nicht daran halten. Seriöse Crawler beachten die in der Datei aufgeführten Regeln allerdings.
favicon.ico ist ein Icon, das dem Text im Browser-Tab vorangestellt wird.
Wenn Sie die Produktivversion mittels npm run build
oder npm run preview
erstellen möchten, muss es eine Datei index.html
geben,
da Vite defaultmäßig die Erstellung von Single-Page-Anwendungen unterstützt. Defaultmäßig wird nur die Datei index.html
in den Ordner build
übernommen. Das ist für unsere Zwecke in Ordnung, da wir mit Hilfe von Vue eine Single-Page-Anwendung erstellen werden.
Die Inhalte einer derartigen Anwendung werden per JavaScript nachgeladen. Man benötigt also tatsächlich nur eine HTML-Datei.
Achtung, die Datei body.css
wird beim Packen von vite durch eine neue Datei mit zufälligem Namen ersetzt. Das zugehörige link
-Element erhält keine id. Daher müssen wir in der Datei main.js
die Zeile
document.getElementById('body_css').rel = 'stylesheet';
löschen. Fügen Sie dafür folgende Zeile am Anfang in main.js
ein:
import '../css/body.css'
ECMAScript unterstützt den expliziten Import von CSS- oder JSON-Dateien nicht. Vite erweitert EXMAScript in dieser Hinsicht. Allerdings wird die von Vite erzeugte CSS-Datei, die einen zufälligen Namen erhält, statisch in die generierte Index-HTML-Datei eingafügt und damit synchron geladen.
Wir üssem uns noch überlegen, wie wir Vite dazu bringen, die von ihm generierte CSS-Datei asynchron zu laden.
Anmerkung für Nicht-Single-Page-Anwendungen
Sie können auch eine Anwendung mit diversen HTML-Dateien mit Hilfe von Vite bündeln und komprimieren. Dafür müssten Sie allerdings
eine so genannte Rollup-Konfiguration in die Datei vite.config.js
einfügen.
- https://support.glitch.com/t/only-index-html-is-generated-on-build/51574/6
- https://rollupjs.org/configuration-options/
- https://rollupjs.org/configuration-options/#input
Verbesserungen (Version 03)
head.css
inline und body.css
dynamisch laden
Zur Erinnerung:
- 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 mittels des Style-Tags direkt in den Head-Bereich der HTML-Seite eingefügt werden. - Die restlichen CSS-Anweisungen werden in die Datei
body.css
eingetragen. - Dies Datei sollte asynchron, d. h. parallel zum Body-Element geladen werden.
Verwenden Sie im Style-Tag die Import-Anweisung, um die Datei css/head.css
einzubinden. Dadurch wird erreicht, dass der Inhalt dieser Datei beim Bündeln direkt in die HTML-Datei eingebunden wurde (inline) und der Browser diese Datei nicht zusätzlich zu laden braucht.
<style>
@import url("css/head.css");
</style>
Wie wir gesehen haben, wird der Inhalt Datei css/body
bislang noch synchron geladen, obwohl wir
den zugehörigen Import-Befehl in die Datei main.js
eingefügt haben, die asynchron geladen wird..
In Vite gibt es jedoch eine Möglichkeit, CSS-Dateien dynamisch zu laden. Wenn man das Vite-Plugin cssInjectedByJsPlugin
verwendet, werden die CSS-Dateien von Vite nicht statisch in die HTML-Datei eingefügt, sondern in der JavaScript-Datei dynamisch zur Laufzeit im Browser geladen und erst im Browser in die HTML-Datei eingefügt. Das zugehörige NPM-Package müssen Sie zunächst installieren:
npm i -D vite-plugin-css-injected-by-js
Anschließend wird das Plugin in vite.config.js
konfiguriert.
import cssInjectedByJsPlugin from 'vite-plugin-css-injected-by-js'
export default
{ root: 'src',
publicDir: '../public',
base: '',
build:
{ outDir: '../web',
minify: true,
},
plugins:
[ cssInjectedByJsPlugin(),
]
}
Schreiben Sie die Hello-World-App entsprechend um.
HTML komprimieren
Wenn man sich die Dateien im Web-Ordner ansieht, bemerkt man, dass nur die JavaScript- und CSS-Dateien gebündelt und verkleinert werden. Die HTML wurde dagegen nicht komprimiert. Um dies zu erreichen, benötigt man ein weiteres Vite-Plugin. Das zugehörige NPM-Package wird ebenfalls installiert:
npm i -D vite-plugin-minify
Anschließend muss das Plugin wieder in vite.config.js
konfiguriert werden, in dem es zunächst importiert wird:
import { ViteMinifyPlugin } from 'vite-plugin-minify'
Anschließend wird der Initialisierungsbefehl in das Array plugins
eingefügt. Diesmal wird allerdings zusätzlich ein
Konfigurationsparameter übergeben, der die Komprimierung der HTML-Datei veranlasst:
ViteMinifyPlugin({}),
Der HTML-Minimizer komprimiert auch CSS-Anweisungen, die im HTML-Code enthalten sind. In unseren Fall ist das der Inhalt der Datei
head.css
, der wegen des @import
-Befehls von Vite direkt in die HTML-Datei eingefügt wird.
Achtung!
Früher habe ich vite-plugin-html
verwendet. Allerdings funktioniert bei Verwendung dieses Plugins der Vite-Proxy nicht mehr (Query Parameter und Argumente in der URL werden nicht weitergeleitet).
Lizenzkommentare entfernen
Ein Problem besteht noch. Die Lizenzkommentare werden in die komprimierten CSS- und JavaScript-Dateien eingefügt. Das kann gewünscht sein, kostet aber Platz. Das Problem kann man zumindest für JavaScript-Dateien beheben, indem man den Minimizer terser
verwendet. Dieser bietet zahlreiche Konfigurationsmöglichkeiten an (ist allerdings beim Komprimieren wesentlich langsamer als der Standard-Minimizer). Schreiben Sie in der Datei
vite.config.js
an Stelle von
minify: true,
folgende Konfigurationsanweisungen:
minify: 'terser',
terserOptions:
{ output: { comments: false } },
Für den CSS-Minimizer habe ich bislang keine entsprechenden Optionen gefunden. Schade. Aber das Problem lässt sich ganz einfach beheben, indem man in CSS-Dateien keine JSDoc-Kommentare verwendet (was ja durchaus auch sinnvoll ist, da CSS keinen JavaScript-Code enthält). Anstelle von
/**
* @author Wolfgang Kowarschick <kowa@hs-augsburg.de>
* @copyright 2016-2025
* @license MIT
*/
können Sie beispielsweise
/*
* author Wolfgang Kowarschick <kowa@hs-augsburg.de>
* copyright 2016-2025
* license MIT
*/
schreiben.
Alias-Pfade in vite.config.js
Fügen Sie in die Datei vite.config.js
unter dem Tag resolve
ein paar Pafdaliases ein:
@
: Abkürzung für den Ordner./src
als root/css
: Abkürzung für den Ordner./src/css
/js
: Abkürzung für den Ordner./src/js
Nun können Sie in der Datei index.html
und der Javascript-Datei die absoluten Pfade
@/...
, /css/...
und /js/...
verwenden. Beim Straten der Anwendung mittels
npm run dev
und Bündeln der Anwendung
mittels npm run build
werden die Pfade von Vite gemäß den Alias-Definitionen aufgelöst.
Ersetzen Sie die relativen Pfade in den Dateien durch geeignete absolute Pfade.
import cssInjectedByJsPlugin from 'vite-plugin-css-injected-by-js'
import { ViteMinifyPlugin } from 'vite-plugin-minify'
import { fileURLToPath, URL } from 'url'
export default
{ root: 'src',
publicDir: '../public',
base: '',
build:
{ outDir: '../web',
minify: 'terser',
terserOptions:
{ output: { comments: false } },
},
plugins:
[ cssInjectedByJsPlugin(),
ViteMinifyPlugin({}),
],
resolve:
{ alias:
{ '@': fileURLToPath(new URL('./src', import.meta.url)),
'/css': fileURLToPath(new URL('./src/css', import.meta.url)),
'/js': fileURLToPath(new URL('./src/js', import.meta.url)),
}
},
}
Fortsetzung des Tutoriums
Sie sollten nun Teil 6 des Tutoriums bearbeiten. In diesem Teil des Tutoriums wird ausgenutzt, dass CSS nun auch Variablendefinitionen (eigentlich handelt es sich um Konstantendefinitionen) und den Import-Befehl unterstützt. Damit kann man das Prinzip “Don't Repeat Yourself” (DRY) auch für CSS-Dateien umsetzen.
Quellen
- Kowarschick (WebProg): Wolfgang Kowarschick; Vorlesung „Web-Programmierung“; Hochschule: Hochschule Augsburg; Adresse: Augsburg; Web-Link; 2024; Quellengüte: 3 (Vorlesung)