VueJS-3-Tutorial – Komponenten und Props mit defineProps
geprüft am 2 September 2026 · 5 Min.
Eine Komponente ist eine .vue-Datei mit eigenem Template, eigenem Zustand und eigener Logik. Importier sie in <script setup>, um sie direkt im Template zu verwenden. Die Elternkomponente gibt ihr Daten über Props weiter, die mit defineProps deklariert und mit dem Doppelpunkt gebunden werden.
Seit sieben Kapiteln steckt alles in einer einzigen Datei. Das skaliert nicht. Eine Komponente ist ein eigenständiges Stück Oberfläche mit eigenem Template, eigenem Zustand und eigener Logik, das sich wiederverwenden und verschachteln lässt.
Die Produktkarte herauslösen
Leg src/components/ProductDisplay.vue an und verschieb alles dorthin, was das Produkt betrifft: das Bild, den Titel, die Verfügbarkeit, die Details, die Farbpunkte, den Button.
<script setup>
import { computed, ref } from 'vue'
const product = ref('T-Shirt')
const brand = ref('Gekkode')
const selectedVariant = ref(0)
const details = ref(['60 % coton', '30 % laine', '10 % polyester'])
const variants = ref([
{ id: 2234, color: '#2563eb', image: '/images/t-shirt-bleu.svg', quantity: 20 },
{ id: 2235, color: '#dc2626', image: '/images/t-shirt-rouge.svg', quantity: 0 }
])
const title = computed(() => `${brand.value} ${product.value}`)
const image = computed(() => variants.value[selectedVariant.value].image)
const inStock = computed(() => variants.value[selectedVariant.value].quantity > 0)
function updateVariant(index) {
selectedVariant.value = index
}
</script>
<template>
<div class="product-display">
<div class="product-image">
<img :src="image" :alt="title">
</div>
<div class="product-info">
<h1>{{ title }}</h1>
<p v-if="inStock">En stock</p>
<p v-else>En rupture</p>
<ul>
<li v-for="detail in details" :key="detail">{{ detail }}</li>
</ul>
<div class="variants-wrapper">
<div
v-for="(variant, index) in variants"
:key="variant.id"
class="color-circle"
:style="{ backgroundColor: variant.color }"
@mouseover="updateVariant(index)"
></div>
</div>
<button class="button" :class="{ disabledButton: !inStock }" :disabled="!inStock">
Ajouter au panier
</button>
</div>
</div>
</template>Der Warenkorb wandert nicht mit: er gehört nicht zum Produkt. App.vue schrumpft auf:
<script setup>
import { ref } from 'vue'
import ProductDisplay from './components/ProductDisplay.vue'
const cart = ref(0)
</script>
<template>
<div class="cart">Panier ({{ cart }})</div>
<ProductDisplay />
</template>Eine in <script setup> importierte Komponente lässt sich direkt im Template verwenden: keine zusätzliche Deklaration nötig.
Das ist normal und genau das Thema des nächsten Kapitels. cart lebt jetzt in der Elternkomponente, außerhalb der Reichweite der Kindkomponente. Wir haben außerdem das @click vom Button entfernt: hätten wir es stehen lassen, gäbe es einen Fehler, denn addToCart existiert in dieser Komponente nicht mehr.
Eine Komponente benennen
Zwei Konventionen bestehen nebeneinander. In PascalCase, <ProductDisplay />, unterscheidest du eine Komponente auf einen Blick von einem HTML-Tag, und der Editor springt zu ihrer Definition. In kebab-case, <product-display></product-display>, bleibst du zu einem Template kompatibel, das direkt in einer HTML-Seite steht.
In einer .vue-Datei ist PascalCase die bessere Wahl.
Eine Komponente hat ihren eigenen Gültigkeitsbereich
Setz drei Karten auf einmal ein:
<template>
<ProductDisplay />
<ProductDisplay />
<ProductDisplay />
</template>Es erscheinen drei Karten, unabhängig voneinander: über einen Farbpunkt der ersten zu fahren lässt die anderen unberührt. Jede Instanz führt ihr eigenes <script setup> aus und besitzt ihr eigenes selectedVariant.
Jede Karte behält auch ihre eigene ausgewählte Variante.
Genau deshalb wird der Zustand einer Komponente in einer Funktion deklariert. Wäre variants ein einfaches Objekt auf Modulebene, kämen sich die drei Karten gegenseitig in die Quere.
Entfern die beiden überzähligen Karten, bevor es weitergeht.
Die Props
Der abgeschottete Gültigkeitsbereich wirft eine Frage auf: wie gibt die Elternkomponente eine Information an das Kind weiter? Über ein Prop, ein eigenes Attribut, das die Komponente ausdrücklich annimmt.
Nehmen wir den Versand dazu, für Premium-Kunden kostenlos. Die Information gehört der Elternkomponente:
<script setup>
const premium = ref(true)
</script>
<template>
<div class="cart">Panier ({{ cart }})</div>
<ProductDisplay :premium="premium" />
</template>Und das Kind deklariert sie:
<script setup>
const props = defineProps({
premium: { type: Boolean, required: true }
})
const shipping = computed(() => (props.premium ? 'Gratuite' : '2,99 €'))
</script>
<template>
<p>Livraison : {{ shipping }}</p>
</template>defineProps steht in <script setup> ohne Import bereit: es ist ein Compiler-Makro, keine gewöhnliche Funktion.
Im Template schreibst du direkt {{ premium }}. Im Skript führt der Weg über das zurückgegebene Objekt: props.premium.
Props ordentlich deklarieren
Die ausführliche Form erlaubt eine Validierung:
const props = defineProps({
premium: { type: Boolean, required: true },
titre: { type: String, default: 'Sans titre' },
tailles: { type: Array, default: () => [] },
note: {
type: Number,
validator: (valeur) => valeur >= 0 && valeur <= 5
}
})Der Standardwert eines Arrays oder eines Objekts muss von einer Funktion zurückgegeben werden, sonst teilten sich alle Instanzen dasselbe Objekt.
:premium="premium" übergibt den Wert der Variablen. premium="premium", ohne Doppelpunkt, übergibt den String "premium". Da ein nicht leerer String wahr ist, bleibt der Bug unbemerkt, bis der Wert eines Tages falsch sein müsste. Vue warnt in der Konsole übrigens, dass Boolean erwartet und String empfangen wurde.
Props gehen nach unten, nicht nach oben
Ein Prop fließt nur in eine Richtung: von der Eltern- zur Kindkomponente. Das Kind darf es nicht verändern, Vue gibt eine Warnung aus, wenn du es versuchst.
Braucht das Kind eine veränderbare lokale Fassung, kopier sie in ein ref. Muss es eine Änderung in der Elternkomponente auslösen, schickt es ihr ein Signal: darum geht es im nächsten Kapitel.
Props werden in der Option props deklariert, mit derselben Validierungssyntax, und über this.premium gelesen. Kindkomponenten müssen zusätzlich in der Option components registriert werden.
ProductDetails.vue aus, die das Array als Prop bekommt. Zeig danach zwei Produktkarten an und prüf, dass jede von ihnen tatsächlich ihre eigene ausgewählte Variante hat.Häufige Fehler
premium="premium" übergibt den String „premium“, der immer wahr ist. Schreib :premium="premium". Vue meldet übrigens: Boolean erwartet, String empfangen.