Modul 3: Komponenten

Props

Daten von Eltern- an Kind-Komponenten weitergeben — typsicher und vorhersagbar.

Was sind Props?

Props (kurz für Properties) sind der Mechanismus, mit dem eine Eltern-Komponente Daten an eine Kind-Komponente übergibt. Sie bilden die öffentliche API einer Komponente — vergleichbar mit Methodenparametern in Ruby.

🛤️

Rails-Vergleich: Partials mit locals

In Rails übergibst du Daten an Partials über locals: <%= render "card", title: "Hallo", color: "blue" %>. Vue Props funktionieren genauso — nur deklarativ und typsicher.

Props definieren mit defineProps

In der Composition API deklarierst du Props mit dem Compiler-Makro defineProps. Es braucht keinen Import — Nuxt/Vue erkennt es automatisch.

GreetingCard.vue
<script setup lang="ts">
// TypeScript-Syntax (empfohlen)
defineProps<{
  title: string
  subtitle?: string
  count: number
}>()
</script>

<template>
  <div>
    <h2>{{ title }}</h2>
    <p v-if="subtitle">{{ subtitle }}</p>
    <span>Anzahl: {{ count }}</span>
  </div>
</template>
💡

TypeScript-Generics bevorzugen

Nutze die generische Syntax defineProps<{ ... }>() statt der Runtime-Variante. So bekommst du volle IDE-Unterstützung und Compile-Time-Checks.

Typvalidierung & Standardwerte

Vue prüft Props zur Laufzeit und warnt in der Konsole, wenn Typen nicht stimmen. Mit withDefaults kannst du Standardwerte setzen.

UserBadge.vue
<script setup lang="ts">
interface Props {
  name: string          // Pflichtfeld
  role?: string         // Optional
  level?: number        // Optional
  isActive?: boolean    // Optional
}

const props = withDefaults(defineProps<Props>(), {
  role: 'Mitglied',
  level: 1,
  isActive: true,
})
</script>

<template>
  <div :class="{ 'opacity-50': !isActive }">
    <strong>{{ name }}</strong>
    <span class="badge">{{ role }} (Stufe {{ level }})</span>
  </div>
</template>
Verschiedene Typen übergeben
<template>
  <!-- String -->
  <UserCard name="Anna" />

  <!-- Number (v-bind nötig!) -->
  <UserCard :level="5" />

  <!-- Boolean (Kurzform = true) -->
  <UserCard is-active />
  <UserCard :is-active="false" />

  <!-- Array -->
  <TagList :tags="['Vue', 'TypeScript', 'Nuxt']" />

  <!-- Object -->
  <ProfileCard :user="{ name: 'Max', age: 28 }" />

  <!-- Dynamisch mit v-bind -->
  <UserCard v-bind="userProps" />
</template>

Einweg-Datenfluss

Props fließen immer von oben nach unten. Eine Kind-Komponente darf Props niemals direkt verändern. Wenn du den Wert lokal anpassen willst, erstelle eine Kopie mit ref() oder computed().

LocalCopy.vue
<script setup lang="ts">
const props = defineProps<{
  initialCount: number
}>()

// ✅ Lokale Kopie erstellen
const count = ref(props.initialCount)

// ✅ Oder als computed
const doubled = computed(() => props.initialCount * 2)

// ❌ NICHT: props.initialCount = 42
</script>
⚠️

Props nicht mutieren!

Vue gibt eine Warnung aus, wenn du versuchst, einen Prop direkt zu verändern. Erstelle stattdessen immer eine lokale Kopie oder nutze emit, um den Eltern-Wert zu aktualisieren.

Pflichtfelder (required)

In TypeScript-Syntax sind Props ohne ? automatisch Pflichtfelder. Vue warnt, wenn sie beim Verwenden der Komponente fehlen.

ProductCard.vue
<script setup lang="ts">
defineProps<{
  // Pflichtfelder (kein ?)
  id: number
  name: string

  // Optionale Felder (mit ?)
  description?: string
  imageUrl?: string
}>()
</script>

<!-- Verwendung: -->
<!-- ✅ <ProductCard :id="1" name="Widget" /> -->
<!-- ❌ <ProductCard name="Widget" />  → Warnung: id fehlt -->

Interaktive Demo

Passe die Props der Karten-Komponente live an — Titel, Farbe und Größe:

Konfigurierbare Karte
Interaktiv
Meine Karte

Farbe: {{ demoColor }} · Größe: {{ demoSize }}

<InfoCard title="Meine Karte" color="blue" size="md" />

Zusammenfassung

ℹ️

Kernpunkte

  • defineProps<T>() definiert die Schnittstelle deiner Komponente
  • withDefaults() setzt Standardwerte
  • Props fließen immer einweg — von Parent zu Child
  • Pflichtfelder werden durch TypeScript-Syntax erzwungen
  • Props nie direkt verändern — lokale Kopie erstellen