Appearance
Vue Commands
Ziel in diesem Versuch ist es, den client der Commands Applikation in VueJS
zu implementieren und den Server beizubehalten.
Die umfängliche Dokumentation zu VueJS finden Sie hier https://vuejs.org/guide/quick-start.html.
Hello World Template erstellen
Für folgende Schritte sollten Sie sicherstellen, dass die Volar
Extension in VS-Code installiert ist, sowie NodeJS und yarn
installiert worden sind.
Vite
Vue ist ein clientseitiges Framework, welches Javascript, HTML und CSS für uns generiert. Unser Source Code wird also neu soft kompiliert, so dass letztendlich aus unserem ganzen Code nur noch ein einziges .js
File erstellt wird. Diesen Prozess heisst auch "bundling". Es existieren unterschiedliche Bundler mit ihren Vor- und Nachteilen (webpack
, rollup
, etc.).
Wir verwenden den Bundler vite
.
Erstellen Sie hierzu im web
Verzeichnis (welches Sie für die anderen Projekte bereits erstellt haben) ein neues Unterverzeichnis mit dem Namen vue-commands
. Öffnen Sie das leere Verzeichnis nun in VS Code.
Im Terminal erstellen Sie nun das VueJS Template mit folgendem Command.
yarn create vite .
Wählen Sie hierbei
Vue
JavaScript
Sie sollten nun einige generierte Files sehen:
Dependencies herunterlanden
Führen Sie nun yarn
ohne Argumente aus um die Dependencies herunterzuladen.
Vite erlaubt es Ihnen einen Dev-Server zu starten, welcher die Seite neu generiert, sobald Sie Änderungen am Source vornehmen. Diesen starten Sie mit:
yarn dev
Der Dev Server wird auf dem Port 5173
gestartet. Sie können mit einem CTRL+Click auf die URL die Seite direkt im Browser vom Terminal her öffnen.
Falls alles geklappt hat, werden Sie mit folgender Seite begrüsst:
Vite + Vue Files
Das Template beinhaltet bereits Zahlreiche Files.
index.hml
ist das Template für die Vue Applikation und beinhaltet den Container für das HTML. Hier können von Hand z.B. Icon und Titel der App geändert werden. Auch können auch falls notwendig zusätzliche Scripts und Styles ausserhalb der Vue App geladen werden.src/main.js
ist der Einstiegspunt in welchem Vue geladen wird und wird in der Regel selten geändert.src/styles.css
ist das globale Stylesheet, welche für alle Vue Components verwendet wird (Theme). Speziefisches Styling sollte innerhalb der Vue Components stattfinden.src/App.vue
ist die Applikation und der Einstiegspunkt um eine Vue App zu erstellen.src/components/
beinhaltet in der Regel Vue Components die in der Applikation modular eingebunden werden.src/assets/
beinhaltet Assets, die on demand geladen werden können.public/
beinhaltet Assets, deren Namen nie geändert werden und die global später unter/
verfügbar sein werden (statische Files).
Bundeln der Webseite
Die Webseite kann mit yarn build
kompiliert / gebundled werden. Hierbei werden statische Files unter dem Folder dist/
erstellt. Diese können nun irgendwo als statische Webseite gehostet werden. Es handelt sich dabei um eine rein clientseitige Webapplikation.
Warum ein JS Framework?
Wir haben bereits gesehen, dass es möglich ist eine clientseitige Webapplikation mit JS, HTML und CSS zu implementieren. Allerdings sind wir an gewisse strukturen gebunden:
- Die Seite kann nicht ohne weiteres mit modularen Komponenten erstellt werden.
- Das Ändern im DOM setzt immer voraus, dass wir mit IDs schaffen um Elemente zu finden. Häufig entfernen wir Elemente und ersetzen diese durch neue.
- Das CSS ist getrennt von JS und wird im Kontext der ganzen Applikation betrachtet. Es wäre elegant, hätten wir das CSS im Kontext einer Komponente.
- Reactivity: Ändern wir einen Variable, so wird das UI nicht direkt upgedated um die Änderung zu repräsentieren. In JS Frameworks haben wir häufig reaktive Objekte, die bei einer Änderung ein Update des DOM auslösen.
- Deklarative Components: Anstelle von
<div>
wäre es besser eine Component mit Argumenten und Handlern zu haben.<Command name="ls" desc="lists files" @click="remove"/>
.
Single File Components (SFC)
Ein Vorteil, welcher Vue gegenüber native JS / HTML bringt, ist das zusammenstellen von Single File Components, die Ähnlich wie bei der OOP zum Ziel haben "separation of concerns" zu erzielen.
Beispiel von https://vuejs.org/guide/introduction.html#single-file-components
vue
<script setup>
import { ref } from 'vue';
const count = ref(0);
</script>
<template>
<button @click="count++">Count is: {{ count }}</button>
</template>
<style scoped>
button {
font-weight: bold;
}
</style>
Commands Component
Wir werden für unsere Applikation einige Zeilen von dem Template ersetzen. Löschen Sie hierzu den Inhalt von src/styles.css
und das File src/components/HelloWorld.vue
.
Ersetzen Sie den Inhalt von src/styles.css
mit:
css
body,
html {
margin: 0;
padding: 0;
width: 100%;
min-height: 100%;
font-family: sans-serif;
}
body {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
Ersetzen Sie den Inhalt von App.vue
mit folgendem Code:
vue
<script setup>
import { ref } from "vue";
const commands = ref([
{
name: "ls",
desc: "lists files",
},
]);
</script>
<template>
<div class="site">
<h1>Commands</h1>
<div class="container">
<div class="command">
<div class="name">{{ commands[0].name }}</div>
<div class="desc">{{ commands[0].desc }}</div>
</div>
</div>
</div>
</template>
<style scoped>
.site {
padding: 50px 0;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
max-width: 800px;
height: 100%;
}
.container {
width: 200px;
display: flex;
flex-wrap: wrap;
}
@media (min-width: 800px) {
.container {
width: 400px;
}
}
@media (min-width: 1000px) {
.container {
width: 600px;
}
}
.command {
border-radius: 5px;
display: flex;
flex-direction: column;
width: 170px;
padding: 10px;
margin: 5px;
background-color: #555;
color: #fff;
cursor: pointer;
}
</style>
Starten Sie den Dev-Server mit yarn dev
.
SFC Components und Composition API
App.vue
ist in drei Teile unterteilt. <script>
, <template>
und <style>
. App selber ist daher eine SFC (Single File Component).
Variablen, welche in <script setup>
deklariert werden, sind in <template>
via Mustaches {{ commands[0].name }}
verfügbar. ref
dient dazu, die Variablen "reactive" zu erstellen. Das heisst, dass Änderungen an deren Werten im UI übernommen werden, ohne dass via getElementById
von Hand ein Element gesucht und geändert werden muss.
Schauen Sie sich den ersten Teil der Dokumentation zu Templates an: https://vuejs.org/guide/essentials/template-syntax.html#template-syntax.
Studieren Sie auch den Teil "Reactive Variables with ref()
" https://vuejs.org/guide/essentials/reactivity-fundamentals.html#reactive-variables-with-ref
.value
Eine Variable, welche mit ref()
erstellt wurde muss immer mit .value
gändert werden.
<template>
ist das Zuhause für den HTML DOM. Allerdings können hier auch Nicht-HTML Elemente verwendet werden, wie wir später sehen werden. So kann eine zusätzliche Komponente (z.B. <Command>
) anstelle von <div>
eingefügt werden.
In <style scoped>
kann CSS für diese eine Komponente deklariert werden. Das Attribute scoped
stellt dabei sicher, dass Selektoren, welche hier eingebettet sind, nicht dem Scope der Komponente verlassen. Das erlaubt das wiederverwenden von gleichen Klassennamen in unterschiedlichen Komponenten.
Listen rendern
Die Applikation ist immer noch rein Clientseitig, wir haben noch keinen Server im betrieb.
Commands ist typischerweise ein Array (welches wir schlussendlich vom Server holen). Ändern Sie den Code nun so, dass das ganze Array gerendert wird in dem Sie sich an folgendem Beitrag orientieren: https://vuejs.org/guide/essentials/list.html#v-for.
Elemente mit Click entfernen
Schauen Sie sich an, wie Sie auf die Commands Elemente einen Click Handler setzen können: https://vuejs.org/guide/essentials/event-handling.html#listening-to-events.
Bauen Sie die App so um, dass ein Element mit einem Click entfernt werden kann.
Tipp: Array filtern
Anstelle, dass Sie tatsächlich Elemente entfernen, können Sie ein neues "gefiltertes" Array erstellen.
Falls Sie nicht sicher sind, wie Sie einen Handler erstellen, finden Sie Dokumentation hier: https://vuejs.org/guide/essentials/event-handling.html#method-handlers
Neue Elemente hinzufügen
In unserer Originalen Applikation hatten wir folgendes Beispiel:
html
<p>Command</p>
<input type="text" id="name" />
<p>Beschreibung</p>
<textarea id="desc"></textarea>
<button id="btn">Neuer Eintrag</button>
Der DOM hatte für name
und desc
eigene IDs für die entsprechenden Inputs. Bei einem Click haben wir die Elemente gesucht, die Werte extrahiert und ein neues Commands Object erstellt.
Hier ist der Ansatz in Reactive Applikationen bedeutend anders. Was wir in Vue verwenden, sind Variablen, welche als Werte bei <input>
und <textarea>
gebunden sind.
Lesen Sie den ersten Teil von https://vuejs.org/guide/essentials/forms.html.
- Erstellen Sie ein reaktives Objekt
currentCommand
mit Attributenname
unddesc
. - Erstellen Sie den DOM für die textarea und den text input. Erstellen Sie auch den Button.
- Binden Sie die Werte von
currentCommand
an die Elemente. - Beim Click auf den Button sollen die Werte nun verwendet werden um ein neues Element an
commands
anzuhängen.
Repo zum nachschlagen
Eine mögliche Lösung finden Sie hier: https://gitlab.fhnw.ch/wlw/vue-commands
Serveranbindung
Binden Sie nun die App an Ihren Commands-Server an. Beachten Sie, dass Sie nicht mehr auf "onload" warten müssen. Sie können direkt in <script>
Requests mit fetch
ausführen.