HTML5-Tutorium: JavaScript: Hello World Vue 04
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
v04: index.html
(HTML validate, CSS validate, Google PageSpeed)
v04a: index.html
(HTML validate, CSS validate, Google PageSpeed)
Git-Repository, git checkout v04
/git checkout v04a
(JavaScript)
Anwendungsfälle (Use Cases)
Gegenüber dem ersten, zweiten und dritten Teil des Vue-Tutoriums ändern sich die die Anwendungsfälle nicht. Die Anwendung leistet also genau dasselbe wie zuvor.
Aufgabe
Aufgabe: Modularisieren Sie HelloWorld.vue
Definieren Sie für die Komponente HelloWorld.vue
zwei Subkomponenten:
component/section/SectionHello.vue
component/section/SectionForm.vue
Zuvor sollten Sie zwei wiederverwendbare Hilfskomponenten definieren:
component/form/FormButton.vue
component/form/FormTextfield.vue
Erstellen eines neuen Projektzweigs
Erstellen Sie einen neuen Projektzweig (branch) innerhalb von hello_world_vue
:
git checkout v03 # Wechsle in den Branch v03
git checkout -b v04 # Klone v03 in einen neuen Branch v04
npm i
Erstellen der Hilfskomponenten
FormButton.vue
Eine parametrisierbare Button-Komponente.
Ein Button hat zwei Properties: Den Typ (button
, reset
...) und den Label, der auf den Button geschrieben werden soll.
Ein Button reagiert auf Klick-Ereignisse. Er reicht diese an die Komponente weiter, in der die aktuelle Button-Komponente eingebaut ist.
In Vue muss man ein entsprechendes Emit-Elemente definieren (defineEmits
) und bei einem Klick
auf den Button die zuvor deinierete Emit-Funktion aufrufen. Als Argument gibt man ihr das Ereignis und gegebenenfalls weitere weitere Werte als Argumente mit.
<!-- src/component/form/FormButton.vue -->
<script setup>
defineProps
({ type: { type: String, default: 'button' },
label: { type: String, default: 'Button' }
})
const
emit = defineEmits(['click']),
click = () => emit('click')
</script>
<template>
<input :value="label" :type @click="click">
</template>
<style scoped>
@import '/css/component/form/FormButton.css';
</style>
Das zugehörige CSS-Modul importiert lediglich ein CSS-Modul für Formulare.
// src/css/component/form/FormButton.css
@import 'Form.css';
Das Formular-CSS-Modul definiert Eigenschaften, die sich mehrere Formular-Elemente teilen.
/* src/css/component/form/Form.css */
@import 'config.css';
label
{ font-family: var(--font-family-serif);
font-size: var(--font-size-p);
}
label, input
{ width: 10em;
font-size: 100%;
display: inline-block;
box-sizing: border-box;
margin: 0.5ex;
}
label
{ text-align: right; }
FormTextfield.vue
Eine parametrisierbare Textfield-Komponente zum Erfassen von Texteingaben.
// src/component/form/FormTextfield.vue
<script setup>
import { useId, computed, onMounted, onUnmounted } from 'vue'
import storeSession from '/store/StoreSession'
import ControllerKeys from '/controller/ControllerKeys'
const
props = defineProps
({ label: { type: String, default: '' },
text: { type: String, default: '' },
autofocus: { type: Boolean, default: false }
}),
inputId = useId(),
session = storeSession(),
emit = defineEmits(['update:text', 'enter']),
text = computed
({ get() { return props.text },
set(p_value) { return emit('update:text', p_value) }
}),
enter = () => emit('enter'),
controllerKeys = new ControllerKeys('Enter', inputId, enter)
session.initAutofocus(props.autofocus ? inputId : null)
onMounted ( () => controllerKeys.add() )
onUnmounted ( () => controllerKeys.remove() )
</script>
<template>
<div>
<label :for="inputId" v-if="label !== ''">{{label}}</label>
<input :id="inputId" type="text" v-model="text" :autofocus>
</div>
</template>
<style scoped>
@import '/css/component/form/FormTextfield.css';
</style>
Mit Hilfe von "computed" wird für die Property "text" bidirektionales Binding ermöglicht.
Die Getterfunktion liest den Wert aus der Property des Aufrufers. Die Setterfunktion
erzeugt bei jeder Änderung ein passendes Ereignis. Der Name des Ereignisse muss laut Vue-Konvention
'update:<Propertyname>'
lauten.
Wie zuvor wird das Formular-CSS-Modul verwendet.
/* src/css/component/form/FormTextfield.css */
@import 'Form.css';
Erstellen der Hello-World-Section-Komponenten
/* src/css/component/section/Section.css */
@import 'config.css';
section
{ h1
{ font-size: var(--font-size-h1);
padding: 0;
margin: 0;
}
}
SectionForm.vue
SectionForm
enthält nur noch ein section
-Element. Die Buttons und das Textfield werden als Komponenten eingebunden.
// src/component/section/SectionForm.vue
<script setup>
import FormButton from '/component/form/FormButton.vue'
import FormTextfield from '/component/form/FormTextfield.vue'
import storeSession from '/store/StoreSession'
import storeGreeting from '/store/StoreGreeting'
const
session = storeSession(),
greeting = storeGreeting(),
sayHello = () => session.section = 'hello'
</script>
<template>
<!-- const in code: 'form' should be passed via a prop -->
<section v-if="session.section==='form'">
<h1>{{greeting.helloStranger}}</h1>
<form>
<div>
<FormTextfield label="What's your name?"
v-model:text="greeting.name"
@enter="sayHello"
autofocus
/>
</div>
<div>
<FormButton label="Reset" type="reset" @click="session.setFocus"/>
<FormButton label="Say Hello" type="button" @click="sayHello"/>
</div>
</form>
</section>
</template>
<style scoped>
@import '/css/component/section/SectionForm.css';
</style>
/* src/css/component/section/SectionForm.css */
@import 'Section.css';
section
{ text-align: center;
margin-left: auto;
margin-right: auto;
h1
{ margin-bottom: 0.5ex; }
}
SectionHello.vue
// src/component/section/SectionHello.vue
<script setup>
import storeSession from '/store/StoreSession'
import storeGreeting from '/store/StoreGreeting'
const
session = storeSession(),
greeting = storeGreeting()
</script>
<template>
<!-- const in code: 'hello' should be passed via a prop -->
<section v-if="session.section==='hello'">
<h1>{{greeting.hello}}</h1>
<p>Welcome to Web Programming!</p>
</section>
</template>
<style scoped>
@import '/css/component/section/SectionHello.css';
</style>
/* src/css/component/section/SectionHello.css */
@import 'Section.css';
section
{ p
{ font-size: var(--font-size-p-large); }
}
Modularisieren der Hello-World-Komponente
Nun besteht die Hello-World-Komponenten nur noch aus zwei Section-Komponenten.
Allerdings muss noch die Startkomponente, das heißt die Komponenten, die zunächst sichtbar sein soll, initialisiert werden.
Dies kann später entfallen, wenn die Stores mit Hilfe von JSON-Dateien initialisiert werden.
<script setup>
import SectionForm from '/component/section/SectionForm.vue'
import SectionHello from '/component/section/SectionHello.vue'
import storeSession from '/store/StoreSession'
const
session = storeSession()
// const in code: session should be initialized via a config file
session.init('form')
</script>
<template>
<SectionForm/>
<SectionHello/>
</template>
<style scoped>
@import '/css/component/HelloWorld.css';
</style>
Das Folgende sind nur Dummy-CSS-Dateien, die aus Symmetriegründen angelegt wurde: Für jede Komponente gibt es gemäß WK-Konvention eine eigene CSS-Datei.
/* src/css/component/component.css */
@import 'config.css';
/* src/css/component/HelloWorld.css */
@import 'component.css';
In der Hello-World Komponente sollten nur die Komponenten statisch geladen werden, die “above the fold” liegen. Das ist hier die Formular-Komponente. Die Hello-Komponente sollte dagegen asynchron geladen werden.
<script setup>
import { defineAsyncComponent, computed } from 'vue'
import SectionForm from '/component/section/SectionForm.vue'
//import SectionHello from '/component/section/SectionHello.vue'
import storeSession from '/store/StoreSession'
const
session = storeSession(),
helloComponent =
computed(() => defineAsyncComponent
(() => import('/component/section/SectionHello.vue')))
// const in code: session should be initialized via a config file
session.init('form')
</script>
<template>
<SectionForm/>
<component :is="helloComponent"/>
</template>
<style scoped>
@import '/css/component/HelloWorld.css';
</style>
Bereinigen der Datei body.css
Die in den obigen CSS-Modulen enthaltenen Inhalte können und sollten
aus der Datei body.css
gelöscht werden.
Fortsetzung des Tutoriums
Sie sollten nun Teil 5 des Vue-Tutoriums bearbeiten. In diesem Tutorium werden die Daten extern aus JSON-Dateien geladen, erst statisch, dann dynamisch. Zum Schluss wird die Anwendung internationalisiert.
Quellen
- Kowarschick (WebProg): Wolfgang Kowarschick; Vorlesung „Web-Programmierung“; Hochschule: Hochschule Augsburg; Adresse: Augsburg; Web-Link; 2024; Quellengüte: 3 (Vorlesung)