Zum Inhalt springen

Template Syntax

Vue nutzt eine HTML-basierte Template-Syntax, die es ermöglicht, das gerenderte DOM deklarativ an die Daten der Komponenten-Instanz zu binden.

Unter der Haube kompiliert Vue die Templates in hochoptimierten JavaScript-Code.

Die einfachste Form der Datenbindung ist die Text-Interpolation mit der “Mustache”-Syntax (doppelte geschweifte Klammern):

<template>
<span>Nachricht: {{ msg }}</span>
</template>

Der Mustache-Tag wird durch den Wert der msg-Property ersetzt und bei jeder Änderung aktualisiert.

Die Mustache-Syntax interpretiert Daten als reinen Text. Um echtes HTML auszugeben, verwende die v-html Direktive:

<script setup>
import { ref } from 'vue';
const rawHtml = ref('<span style="color: red">Rot</span>');
</script>
<template>
<p>Text: {{ rawHtml }}</p>
<p>HTML: <span v-html="rawHtml"></span></p>
</template>

Mustaches können nicht in HTML-Attributen verwendet werden. Stattdessen nutzen wir die v-bind Direktive:

<script setup>
import { ref } from 'vue';
const imageUrl = ref('/profile.jpg');
const isDisabled = ref(true);
</script>
<template>
<img v-bind:src="imageUrl" />
<button v-bind:disabled="isDisabled">Klick mich</button>
</template>

Da v-bind sehr häufig verwendet wird, gibt es eine Kurzschreibweise mit ::

<template>
<!-- Vollständige Syntax -->
<img v-bind:src="imageUrl" />
<!-- Kurzschreibweise -->
<img :src="imageUrl" />
<!-- Dynamische Attributnamen -->
<button :[attributeName]="value">...</button>
</template>

Bei Boolean-Attributen bestimmt der Wert, ob das Attribut vorhanden ist:

<script setup>
import { ref } from 'vue';
const isButtonDisabled = ref(true);
</script>
<template>
<!-- disabled wird gerendert wenn isButtonDisabled truthy ist -->
<button :disabled="isButtonDisabled">Button</button>
</template>

Bei null, undefined oder false wird das Attribut nicht ins HTML aufgenommen.

Mit v-bind ohne Argument können mehrere Attribute auf einmal gebunden werden:

<script setup>
import { reactive } from 'vue';
const inputAttrs = reactive({
id: 'username',
type: 'text',
placeholder: 'Benutzername eingeben'
});
</script>
<template>
<input v-bind="inputAttrs" />
<!-- Entspricht: <input id="username" type="text" placeholder="..."> -->
</template>

Vue unterstützt JavaScript-Ausdrücke in allen Datenbindungen:

<template>
{{ number + 1 }}
{{ ok ? 'JA' : 'NEIN' }}
{{ message.split('').reverse().join('') }}
<div :id="`list-${id}`"></div>
</template>

Direktiven sind spezielle Attribute mit dem v- Präfix. Sie wenden reaktives Verhalten auf das DOM an.

<template>
<!-- v-if: Bedingte Darstellung -->
<p v-if="seen">Jetzt sichtbar</p>
<!-- v-for: Listen rendern -->
<li v-for="item in items" :key="item.id">{{ item.text }}</li>
<!-- v-show: CSS-basiertes Ein-/Ausblenden -->
<p v-show="isVisible">Immer im DOM</p>
<!-- v-model: Zwei-Wege-Bindung -->
<input v-model="message" />
</template>

Mit der v-on Direktive lauschen wir auf DOM-Events:

<script setup>
import { ref } from 'vue';
const count = ref(0);
function increment() {
count.value++;
}
</script>
<template>
<!-- Vollständige Syntax -->
<button v-on:click="increment">+1</button>
<!-- Kurzschreibweise mit @ -->
<button @click="increment">+1</button>
<!-- Inline Handler -->
<button @click="count++">+1</button>
</template>

Vue bietet Event-Modifier für häufige Aufgaben:

<template>
<!-- stopPropagation -->
<button @click.stop="doThis">...</button>
<!-- preventDefault -->
<form @submit.prevent="onSubmit">...</form>
<!-- Modifier verketten -->
<a @click.stop.prevent="doThat">...</a>
<!-- Nur einmal auslösen -->
<button @click.once="doOnce">...</button>
<!-- Key Modifier -->
<input @keyup.enter="submit" />
</template>

v-model erstellt eine Zwei-Wege-Bindung für Formular-Elemente:

<script setup>
import { ref } from 'vue';
const message = ref('');
const checked = ref(false);
const selected = ref('');
</script>
<template>
<input v-model="message" placeholder="Text eingeben" />
<p>Nachricht: {{ message }}</p>
<input type="checkbox" v-model="checked" />
<select v-model="selected">
<option value="a">A</option>
<option value="b">B</option>
</select>
</template>

v-model unterstützt eingebaute Modifier:

<template>
<!-- Synchronisiert nach "change" statt "input" -->
<input v-model.lazy="msg" />
<!-- Automatische Typkonvertierung zu Number -->
<input v-model.number="age" type="number" />
<!-- Whitespace trimmen -->
<input v-model.trim="message" />
</template>
<script setup>
import { ref } from 'vue'
const isLoggedIn = ref(false)
const userRole = ref('guest')
</script>
<template>
<!-- Einfaches v-if -->
<p v-if="isLoggedIn">Willkommen zurück!</p>
<!-- v-if / v-else -->
<button v-if="isLoggedIn" @click="logout">Logout</button>
<button v-else @click="login">Login</button>
<!-- v-if / v-else-if / v-else -->
<p v-if="userRole === 'admin'">Admin-Bereich</p>
<p v-else-if="userRole === 'user'">Benutzer-Bereich</p>
<p v-else>Bitte einloggen</p>
</template>

Um mehrere Elemente bedingt zu rendern, verwende <template> als unsichtbaren Wrapper:

<template>
<template v-if="showDetails">
<h2>Details</h2>
<p>Beschreibung...</p>
<button>Aktion</button>
</template>
</template>
<p v-if="visible">Text</p>
  • Element wird aus DOM entfernt
  • Höhere Toggle-Kosten
  • Für selten wechselnde Zustände
  • Unterstützt v-else
<script setup>
import { ref } from 'vue'
const items = ref([
{ id: 1, text: 'Lernen' },
{ id: 2, text: 'Üben' },
{ id: 3, text: 'Meistern' }
])
</script>
<template>
<ul>
<!-- :key ist wichtig für Performance und korrektes Verhalten! -->
<li v-for="item in items" :key="item.id">
{{ item.text }}
</li>
</ul>
</template>
<template>
<!-- Mit Index -->
<li v-for="(item, index) in items" :key="item.id">
{{ index + 1 }}. {{ item.text }}
</li>
<!-- Über einen Bereich -->
<span v-for="n in 10" :key="n">{{ n }}</span>
<!-- Rendert: 1 2 3 4 5 6 7 8 9 10 -->
<!-- Über Objekt-Properties -->
<div v-for="(value, key) in { name: 'Max', age: 30 }" :key="key">
{{ key }}: {{ value }}
</div>
</template>

v-if und v-for sollten nicht auf demselben Element verwendet werden:

<template>
<!-- FALSCH: v-if und v-for zusammen -->
<li v-for="item in items" v-if="item.isActive" :key="item.id">
{{ item.text }}
</li>
<!-- RICHTIG: Computed Property für Filterung -->
<li v-for="item in activeItems" :key="item.id">
{{ item.text }}
</li>
<!-- ODER: v-if auf Wrapper-Element -->
<template v-for="item in items" :key="item.id">
<li v-if="item.isActive">{{ item.text }}</li>
</template>
</template>

Offizielle Vue Dokumentation:

API Referenz: