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 | Vue 5 | Vue 6
Musterlösung
v03: index.html
(HTML validate, Google PageSpeed)
v04: index.html
(HTML validate, Google PageSpeed)
v05: index.html
(HTML validate, Google PageSpeed;
Modularisierung analog zu Hello World 04 v02)
v06: index.html
(HTML validate, Google PageSpeed;
Konfiguration mittels JSON analog zu Hello World 04 v03)
Git-Repository
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.
In diesem Teil des Tutoriums geht es 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:https://tha.de/~kowa/hello_world_05
). - Wenn Sie das Projekt mittels
npm run build
für den Produktivbetrieb erstellen lassen, wird das Ergebnis in den Ordnerweb
geschrieben. 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 code>prexyz definiert wurde. Nach
npm run xyz
automatisch npm run postxyz
ausgeführt, sofern code>postxyz definiert wurde.
Restrukturierung des Projekts
Wenn man nun das Projekt mittels npm run dev
startet, wird man nichts sehen. Der Grund ist, dass der Browser keine Datei index.html
findet, da der Vite-Server nun den Inhalt des Ordners src
ausliefert. Diesen gibt es aber noch gar nicht.
Die Ordnerstruktur des Projekts muss an die Konfigurationsdatei angepasst werden.
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.
<img src="/vite.svg" class="logo" alt="Vite logo" />
unverändert. Die anderen Pfade müssen dagegen eventuell angepasst werden:
// src/index.html
<script type="module" 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 (Build, Version 02)
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 03)
Ersetzen Sie nun die Vite-Default-App durch die Hello-World-App aus dem 4. Teil des Tutoriums.
Wenn Sie die Produktivversion mittels npm run build
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 heißt, wenn man beispielsweise die Musterlösung von von WK_HelloWorld04 in den (zuvor geleerten) Ordner src
kopiert,
funktioniert diese Anwendung zwar im Developermoduls (npm run dev
), aber nicht im Produktivmodus. Wenn man (für dieses Beispiel)
npm run build
ausführt, erhält man die Fehlermeldung
Could not resolve entry module "src/index.html".
Das Problem ist, dass es keine Datei index.html
gibt, dafür aber drei Dateien index0.html
, index1.html
oder index2.html
.
Die Lösung ist in diesem Fall ganz einfach. Nennen Sie eine der drei Dateien in index.html
um. Im Ordner web
wird dann durch npm run build
auch nur eine HTML-Datei namens index.html
erzeugt. 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.
Anmerkung
Sie können auch eine Anwendung mit diversen HTML-Dateien mit Hilfe von Vite bündeln und komprimieren. Dafür müssen 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 04)
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>
Der Hack, der im Teil 4 zu asynchronen Laden von body.css
angewendet wurde, funktioniert leider nicht mehr, da Vite das Element folgendermaßen transformiert.
<link rel="stylesheet" href="data:text/css;base64,LyoNCiAqIEBhdXRob3IgICAgV29s...=="
media="none" onload="this.media='all'"
/>
Das heißt, der Inhalt der Datei body.css
wird Base64-enkodiert und direkt in das Link-Element eingefügt. Der ganze Inhalt der CSS-Datei ist jetzt Bestandteil der Datei index.html
und wird vor den Body-Inhalten geladen. Ziel des Hack war es ja gerade, dies zu vermeiden.
In Vite gibt es jedoch eine andere Möglichkeit, CSS-Dateien dynamisch zu laden. Man kann sie mittels JavaScript laden (vgl. [main.js
der Vite-App]).
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 weitere 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-2024
* @license MIT
*/
können Sie beispielsweise
/*
* author Wolfgang Kowarschick <kowa@hs-augsburg.de>
* copyright 2016-2024
* 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 { createHtmlPlugin } from 'vite-plugin-html'
import { fileURLToPath, URL } from 'url'
export default
{ root: 'src',
publicDir: '../public',
base: '',
build:
{ outDir: '../web',
minify: 'terser',
terserOptions:
{ output: { comments: false } },
},
plugins:
[ cssInjectedByJsPlugin(),
createHtmlPlugin({ minify: true }),
],
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 von Vite SCSS, LESS etc. unterstützt wird. 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)