Drie Basis Tools
In Vue's template (HTML) heb je drie tools nodig om data en gedrag aan je HTML te koppelen:
- Mustache
{{ }}— toon data als tekst - v-bind (afkorting
:) — koppel data aan een HTML attribuut - v-on (afkorting
@) — luister naar events
Onthoud deze drie:
{{ value }} <!-- tekst in HTML -->
:attr="value" <!-- attribuut binden -->
@event="handler" <!-- event afhandelen -->
Mustache {{ }} — Tekst tonen
De dubbele accolades (mustache) gebruik je om de waarde van een variabele als tekst in je template te zetten.
<script setup>
import { ref } from 'vue'
const name = ref('Jason')
const age = ref(25)
</script>
<template>
<h1>Hallo {{ name }}!</h1>
<p>Je bent {{ age }} jaar oud.</p>
</template>
Resultaat:
<h1>Hallo Jason!</h1>
<p>Je bent 25 jaar oud.</p>
Belangrijk: Mustache werkt alleen in de inhoud van een tag, niet binnen attributen. Voor attributen gebruik je v-bind.
Werkt niet:
<img src="{{ imageUrl }}" /> <!-- ❌ Verkeerd -->
Wel goed (zie v-bind):
<img :src="imageUrl" /> <!-- ✅ -->
v-bind (:) — Attributen binden
Met v-bind koppel je een variabele aan een HTML attribuut. In de praktijk gebruik je bijna altijd de afkorting :.
Volledige vs Korte syntax
<!-- Volledig -->
<img v-bind:src="imageUrl" />
<!-- Kort (zo gebruik je het) -->
<img :src="imageUrl" />
Praktisch voorbeeld
<script setup>
import { ref } from 'vue'
const imageUrl = ref('/photo.jpg')
const altText = ref('Een mooie foto')
const isDisabled = ref(true)
const linkUrl = ref('https://vuejs.org')
</script>
<template>
<img :src="imageUrl" :alt="altText" />
<button :disabled="isDisabled">Klik</button>
<a :href="linkUrl">Vue website</a>
</template>
Regel: Zonder : is het een gewone HTML string. Met : is het JavaScript.
<img src="imageUrl" /> <!-- letterlijk de tekst "imageUrl" -->
<img :src="imageUrl" /> <!-- de waarde van de variabele -->
v-on (@) — Events afhandelen
Met v-on reageer je op gebeurtenissen zoals clicks, inputs en keyboard. De afkorting @ is de standaard.
Volledige vs Korte syntax
<!-- Volledig -->
<button v-on:click="handleClick">Klik</button>
<!-- Kort (zo gebruik je het) -->
<button @click="handleClick">Klik</button>
Praktisch voorbeeld
<script setup>
import { ref } from 'vue'
const count = ref(0)
const increment = () => {
count.value++
}
</script>
<template>
<p>Je hebt {{ count }} keer geklikt</p>
<!-- 1. Functie referentie -->
<button @click="increment">+1</button>
<!-- 2. Inline expressie -->
<button @click="count++">+1 (inline)</button>
<!-- 3. Functie met argument -->
<button @click="count += 5">+5</button>
</template>
Andere events
<input @input="handleInput" />
<form @submit="handleSubmit">...</form>
<div @mouseover="handleHover">Hover me</div>
<input @keyup.enter="handleEnter" /> <!-- met modifier -->
Modifiers zoals .enter, .stop, .prevent zijn handige korte schrijfwijzen. Bekijk de Events pagina voor meer details.
JavaScript Expressies binnen {{ }}
Tussen de mustache mag je elke JavaScript expressie gebruiken — niet alleen variabelen.
<script setup>
import { ref } from 'vue'
const firstName = ref('Anna')
const lastName = ref('Jansen')
const items = ref(['appel', 'banaan', 'kers'])
</script>
<template>
<!-- String concatenatie -->
<p>Volledige naam: {{ firstName + ' ' + lastName }}</p>
<!-- Template literals -->
<p>{{ `Hallo, ${firstName}!` }}</p>
<!-- Methode aanroepen -->
<p>In hoofdletters: {{ firstName.toUpperCase() }}</p>
<!-- Ternary operator -->
<p>{{ items.length > 0 ? 'Er zijn items' : 'Leeg' }}</p>
<!-- Array methodes -->
<p>Aantal: {{ items.length }}</p>
<p>Eerste: {{ items[0] }}</p>
</template>
Wat mag NIET tussen {{ }}:
- Statements:
{{ if (x) { ... } }}❌ - Variabelen declareren:
{{ const x = 5 }}❌
Alleen expressies die een waarde teruggeven.
Class & Style Binding (handige v-bind trucjes)
v-bind heeft slimme features voor class en style. Hier komt Vue's reactivity echt tot zijn recht.
Class binding met object
<script setup>
import { ref } from 'vue'
const isActive = ref(true)
const hasError = ref(false)
</script>
<template>
<div :class="{ active: isActive, error: hasError }">
Hallo
</div>
</template>
Resultaat (omdat isActive true is, hasError false):
<div class="active">Hallo</div>
Class binding met array
<script setup>
const baseClass = 'card'
const variantClass = 'card--large'
</script>
<template>
<div :class="[baseClass, variantClass]">
Inhoud
</div>
</template>
Inline style binding
<script setup>
import { ref } from 'vue'
const color = ref('red')
const fontSize = ref(20)
</script>
<template>
<p :style="{ color: color, fontSize: fontSize + 'px' }">
Gekleurde tekst
</p>
</template>
Tip: Class binding is een van de elegantste features van Vue. Je hoeft geen className ? "active" : "" ternary te schrijven zoals in React — gewoon een object met booleans.
Vue vs React — Side-by-side
Voor wie React al kent: dit is hoe template syntax vertaalt.
Tekst tonen
<!-- React (JSX) -->
<h1>Hallo {name}!</h1>
<!-- Vue -->
<h1>Hallo {{ name }}!</h1>
Attribuut binden
<!-- React (JSX) -->
<img src={imageUrl} alt={altText} />
<!-- Vue -->
<img :src="imageUrl" :alt="altText" />
Event afhandelen
<!-- React (JSX) -->
<button onClick={handleClick}>Klik</button>
<!-- Vue -->
<button @click="handleClick">Klik</button>
Conditioneel class
<!-- React (JSX) -->
<div className={isActive ? "card active" : "card"}>...</div>
<!-- Vue -->
<div :class="['card', { active: isActive }]">...</div>
Het grote verschil: In React gebruik je overal JavaScript-expressies tussen { }. In Vue is er onderscheid: {{ }} voor tekst, : voor attributen, @ voor events. Eenmaal door, is het heel snel te lezen.