Kapitel 8 von 11

VueJS-3-Tutorial – Komponenten und Props mit defineProps

geprüft am 2 September 2026 · 5 Min.

Schnelle Antwort

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.

Am Ende dieses Kapitels, kannst du eine Oberfläche in Komponenten zerlegen und ihnen Daten übergeben.

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.

src/components/ProductDisplay.vue
<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:

src/App.vue
<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.

Der Button füllt den Warenkorb nicht mehr

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:

src/App.vue
<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:

src/App.vue
<script setup>
const premium = ref(true)
</script>

<template>
  <div class="cart">Panier ({{ cart }})</div>
  <ProductDisplay :premium="premium" />
</template>

Und das Kind deklariert sie:

src/components/ProductDisplay.vue
<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:

javascript
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.

Der Doppelpunkt ist Pflicht

: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.

In der Options API

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.

ÜbungLager die Liste der Details in eine Komponente 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

Den Doppelpunkt beim Prop vergessen premium="premium" übergibt den String „premium“, der immer wahr ist. Schreib :premium="premium". Vue meldet übrigens: Boolean erwartet, String empfangen.
Ein Prop in der Kindkomponente ändern Props gehen nur nach unten, Vue warnt in der Konsole. Kopier den Wert in ein lokales ref oder schick ein Event nach oben.
Standardwert eines Arrays oder Objekts Er muss von einer Funktion zurückgegeben werden, sonst teilen sich alle Instanzen dasselbe Objekt.
Newsletter

Neue Tests, Tutorials und Projekte, per E-Mail.

Reproduzierbare Tests, versionierter Code, datierte Ergebnisse. Niemals Spam.