Appearance
Kapitel 19: Komponenten-Slots
📙 Lernziel: Slots meistern - Inhalte flexibel in Komponenten einfügen!
19.1 Was sind Slots?
Slots ermöglichen es, Inhalte in eine Kind-Komponente einzufügen.
Vergleich mit Props:
Props: Daten übergeben (Strings, Zahlen, Objekte)Slots: HTML/Andere Komponenten einfügen
Beispiel-Szenario:
<BaseLayout>
<template #header>
<h1>Überschrift</h1>
</template>
<template #default>
<p>Hauptinhalt</p>
</template>
<template #footer>
<p>Fußzeile</p>
</template>
</BaseLayout>19.2 Standard-Slot (Default Slot)
Einfacher Slot:
vue
<!-- components/BaseButton.vue -->
<script setup>
// Keine Logik nötig
</script>
<template>
<button class="base-button">
<!-- Slot (Standard) -->
<slot></slot>
</button>
</template>
<style scoped>
.base-button {
padding: 10px 20px;
background: #42b883;
color: white;
border: none;
border-radius: 5px;
cursor: pointer;
}
</style>Verwendung:
vue
<script setup>
import BaseButton from './components/BaseButton.vue'
</script>
<template>
<div>
<!-- Einfacher Text -->
<BaseButton>Klick mich!</BaseButton>
<!-- Mit Icon -->
<BaseButton>
<span>🚀</span> Starten
</BaseButton>
<!-- Mit HTML -->
<BaseButton>
<strong>Wichtig!</strong> Klick mich
</BaseButton>
</div>
</template>19.3 Benannte Slots (Named Slots)
Mehrere Slots in einer Komponente:
vue
<!-- components/BaseLayout.vue -->
<script setup>
// Keine Logik nötig
</script>
<template>
<div class="base-layout">
<!-- Header Slot -->
<header>
<slot name="header"></slot>
</header>
<!-- Main Content (Default Slot) -->
<main>
<slot></slot>
</main>
<!-- Footer Slot -->
<footer>
<slot name="footer"></slot>
</footer>
</div>
</template>
<style scoped>
.base-layout {
border: 1px solid #ddd;
border-radius: 8px;
padding: 20px;
margin: 20px 0;
}
header {
border-bottom: 1px solid #ddd;
padding-bottom: 10px;
margin-bottom: 10px;
}
footer {
border-top: 1px solid #ddd;
padding-top: 10px;
margin-top: 10px;
}
</style>Verwendung:
vue
<script setup>
import BaseLayout from './components/BaseLayout.vue'
</script>
<template>
<BaseLayout>
<!-- Benannter Slot (v-slot:header oder #header) -->
<template #header>
<h1>Meine Webseite</h1>
<p>Willkommen!</p>
</template>
<!-- Default Slot -->
<p>Hier ist der Hauptinhalt der Seite.</p>
<p>Noch mehr Inhalt...</p>
<!-- Benannter Slot (v-slot:footer oder #footer) -->
<template #footer>
<p>© 2026 Meine Webseite</p>
<a href="/impressum">Impressum</a>
</template>
</BaseLayout>
</template>19.4 Slot-Bestückung (Scoped Slot)
Problem: Slot-Inhalt hat keinen Zugriff auf Daten der Kind-Komponente.
Lösung: Scoped Slots - Daten an Slot weitergeben.
Beispiel:
vue
<!-- components/BaseList.vue -->
<script setup>
import { ref } from 'vue'
const items = ref(['Apfel', 'Banane', 'Orange'])
</script>
<template>
<ul>
<li v-for="(item, index) in items" :key="index">
<!-- Scoped Slot - Daten weitergeben -->
<slot :item="item" :index="index" :isEven="index % 2 === 0">
<!-- Fallback (wenn kein Slot bereitgestellt wird) -->
{{ item }}
</slot>
</li>
</ul>
</template>
<style scoped>
ul {
list-style: none;
padding: 0;
}
li {
padding: 8px;
border-bottom: 1px solid #ddd;
}
li:nth-child(even) {
background: #f5f5f5;
}
</style>Verwendung:
vue
<script setup>
import BaseList from './components/BaseList.vue'
</script>
<template>
<div>
<h2>Liste mit benutzerdefinierter Darstellung</h2>
<BaseList>
<!-- Scoped Slot (v-slot oder #default) -->
<template #default="{ item, index, isEven }">
<strong>{{ index + 1 }}.</strong>
<span :style="{ color: isEven ? 'green' : 'blue' }">
{{ item }}
</span>
</template>
</BaseList>
</div>
</template>19.5 Fallback-Inhalte (Slot-Defaults)
Slots können Standardinhalte haben:
vue
<!-- components/SubmitButton.vue -->
<script setup>
defineProps({
submitText: {
type: String,
default: 'Absenden'
}
})
</script>
<template>
<button type="submit" class="submit-button">
<!-- Fallback-Inhalt -->
<slot name="icon">
📨 <!-- Standard-Icon -->
</slot>
<span>{{ submitText }}</span>
<slot name="right-icon">
→ <!-- Standard-Icon (rechts) -->
</slot>
</button>
</template>
<style scoped>
.submit-button {
padding: 10px 20px;
background: #42b883;
color: white;
border: none;
border-radius: 5px;
cursor: pointer;
display: flex;
align-items: center;
gap: 8px;
}
</style>Verwendung:
vue
<script setup>
import SubmitButton from './components/SubmitButton.vue'
</script>
<template>
<div>
<!-- Nur mit Fallbacks -->
<SubmitButton submit-text="Senden" />
<!-- Mit benutzerdefinierten Icons -->
<SubmitButton submit-text="Speichern">
<template #icon>💾</template>
<template #right-icon>✅</template>
</SubmitButton>
</div>
</template>19.6 Übung: Card-Komponente mit Slots
Aufgabe: Erstelle eine Card.vue Komponente mit Slots.
Lösung:
vue
<!-- components/Card.vue -->
<script setup>
defineProps({
title: {
type: String,
default: 'Kartentitel'
}
})
</script>
<template>
<div class="card">
<!-- Header Slot -->
<div class="card-header">
<slot name="header">
<h3>{{ title }}</h3>
</slot>
</div>
<!-- Default Slot (Content) -->
<div class="card-content">
<slot>
<p>Kein Inhalt vorhanden.</p>
</slot>
</div>
<!-- Footer Slot -->
<div class="card-footer">
<slot name="footer">
<button>OK</button>
</slot>
</div>
</div>
</template>
<style scoped>
.card {
border: 1px solid #ddd;
border-radius: 8px;
overflow: hidden;
max-width: 400px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
.card-header {
background: #f5f5f5;
padding: 15px;
border-bottom: 1px solid #ddd;
}
.card-header h3 {
margin: 0;
color: #42b883;
}
.card-content {
padding: 15px;
min-height: 100px;
}
.card-footer {
background: #f5f5f5;
padding: 10px 15px;
border-top: 1px solid #ddd;
display: flex;
justify-content: flex-end;
gap: 10px;
}
</style>Verwendung:
vue
<script setup>
import Card from './components/Card.vue'
</script>
<template>
<div>
<h2>Karten-Beispiel</h2>
<!-- Standard (mit Fallbacks) -->
<Card title="Einfache Karte" />
<!-- Mit benutzerdefinierten Slots -->
<Card>
<template #header>
<h3>🎉 Benutzerdefinierte Karte</h3>
</template>
<template #default>
<p>Dies ist ein benutzerdefinierter Inhalt für die Karte.</p>
<ul>
<li>Punkt 1</li>
<li>Punkt 2</li>
<li>Punkt 3</li>
</ul>
</template>
<template #footer>
<button @click="alert('Abbrechen')">Abbrechen</button>
<button @click="alert('Speichern')">Speichern</button>
</template>
</Card>
</div>
</template>✅ Zusammenfassung
In diesem Kapitel hast du gelernt:
- ✅ Was Slots sind (Inhalte in Komponenten einfügen)
- ✅ Standard-Slot (Default Slot)
- ✅ Benannte Slots (Named Slots)
- ✅ Scoped Slots (Daten an Slot weitergeben)
- ✅ Fallback-Inhalte (Slot-Defaults)
- ✅ Praxis:
Card.vueKomponente
🎯 Nächster Schritt: In Kapitel 20 lernst du Komponenten-Lebenszyklus-Hooks!
← Zurück zu Kapitel 18: Provide & InjectWeiter zu Kapitel 20: Lebenszyklus-Hooks →
