Appearance
Kapitel 25: Composition API (Teil 5) - Benutzerdefinierte Composables
📙 Lernziel: Eigene Composables erstellen - Logik wiederverwenden!
25.1 Was sind benutzerdefinierte Composables?
Composables sind JavaScript-Funktionen, die Composition API nutzen, um Logik wiederzuverwenden.
Vorteile:
- ✅ Logik-Wiederverwendung (DRY - Don't Repeat Yourself)
- ✅ Bessere Organisation (verwandte Logik zusammen)
- ✅ Testbarkeit
- ✅ TypeScript-Unterstützung
Beispiel-Szenario:
useCounter.js → Zähler-Logik
useFetch.js → API-Aufrufe
useLocalStorage.js → Lokaler Speicher
useWindowSize.js → Fenstergröße überwachen25.2 Einfaches Composable erstellen: useCounter.js
Schritt 1: Composable-Datei erstellen
javascript
// composables/useCounter.js
import { ref } from 'vue'
export function useCounter(initialValue = 0, step = 1) {
const count = ref(initialValue)
const increment = () => {
count.value += step
}
const decrement = () => {
count.value -= step
}
const reset = () => {
count.value = initialValue
}
return {
count,
increment,
decrement,
reset
}
}Schritt 2: In Komponente verwenden
vue
<script setup>
import { useCounter } from '../composables/useCounter.js'
// Logik wiederverwenden
const { count, increment, decrement, reset } = useCounter(0, 1)
</script>
<template>
<div>
<h2>Zähler: {{ count }}</h2>
<button @click="decrement">-</button>
<span>{{ count }}</span>
<button @click="increment">+</button>
<button @click="reset">Zurücksetzen</button>
</div>
</template>Vorteile:
- ✅ Logik ist gekapselt
- ✅ In mehreren Komponenten wiederverwendbar
- ✅ Einfach zu testen
25.3 Composable mit Parameter: useFetch.js
Beispiel: Composable für API-Aufrufe
javascript
// composables/useFetch.js
import { ref } from 'vue'
export function useFetch(url) {
const data = ref(null)
const error = ref(null)
const isLoading = ref(false)
const fetchData = async () => {
isLoading.value = true
error.value = null
try {
const response = await fetch(url)
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`)
}
data.value = await response.json()
} catch (err) {
error.value = err.message
} finally {
isLoading.value = false
}
}
// Sofort ausführen
fetchData()
return {
data,
error,
isLoading,
fetchData // Manuell neu laden
}
}Verwendung:
vue
<script setup>
import { useFetch } from '../composables/useFetch.js'
// API-Aufruf
const { data: posts, error, isLoading, fetchData } = useFetch('https://jsonplaceholder.typicode.com/posts')
</script>
<template>
<div>
<h2>Posts</h2>
<div v-if="isLoading">Lädt...</div>
<div v-else-if="error">Fehler: {{ error }}</div>
<ul v-else>
<li v-for="post in posts.slice(0, 5)" :key="post.id">
{{ post.title }}
</li>
</ul>
<button @click="fetchData">Neu laden</button>
</div>
</template>25.4 Composable mit anderen Composables kombinieren
Beispiel: useLocalStorage.js mit useCounter.js kombinieren
javascript
// composables/useLocalStorage.js
import { ref, watch } from 'vue'
export function useLocalStorage(key, defaultValue) {
const storedValue = localStorage.getItem(key)
const data = ref(storedValue ? JSON.parse(storedValue) : defaultValue)
// Automatisch in localStorage speichern wenn sich Daten ändern
watch(data, (newValue) => {
localStorage.setItem(key, JSON.stringify(newValue))
}, { deep: true })
const remove = () => {
localStorage.removeItem(key)
data.value = defaultValue
}
return {
data,
remove
}
}Kombinieren:
vue
<script setup>
import { useCounter } from '../composables/useCounter.js'
import { useLocalStorage } from '../composables/useLocalStorage.js'
// Zähler mit localStorage Persistenz
const { data: savedCount, remove } = useLocalStorage('counter', 0)
const { count, increment, decrement, reset } = useCounter(savedCount.value)
// Zählerstand speichern
watch(count, (newValue) => {
savedCount.value = newValue
})
</script>
<template>
<div>
<h2>Zähler: {{ count }}</h2>
<button @click="increment">+</button>
<button @click="decrement">-</button>
<button @click="reset(); remove()">Zurücksetzen & Löschen</button>
<p>(Wert wird im localStorage gespeichert!)</p>
</div>
</template>25.5 Best Practices für Composables
1. Benennungskonvention:
- ✅ Immer mit
use-Präfix (z.B.useCounter,useFetch) - ✅ In
composables/Ordner speichern
2. Rückgabewerte:
- ✅ Objekt mit benannten Eigenschaften zurückgeben
- ✅ Destrukturierung ermöglichen
3. Reaktivität beibehalten:
- ✅
ref()undreactive()verwenden - ✅
toRefs()nutzen, um Reaktivität bei Destrukturierung zu erhalten
4. Fehlerbehandlung:
- ✅ Try-catch Blöcke verwenden
- ✅ Fehler als
ref()zurückgeben
5. Dokumentation:
- ✅ JSDoc-Kommentare hinzufügen
- ✅ Parameter und Rückgabewerte erklären
Beispiel (gut dokumentiert):
javascript
/**
* Composable für Zähler-Funktionalität
* @param {number} initialValue - Startwert (Standard: 0)
* @param {number} step - Schrittweite (Standard: 1)
* @returns {Object} - { count, increment, decrement, reset }
*/
export function useCounter(initialValue = 0, step = 1) {
const count = ref(initialValue)
const increment = () => {
count.value += step
}
const decrement = () => {
count.value -= step
}
const reset = () => {
count.value = initialValue
}
return {
count,
increment,
decrement,
reset
}
}25.6 Übung: useWindowSize.js Composable
Aufgabe: Erstelle ein Composable, das Fenstergröße überwacht.
Lösung:
javascript
// composables/useWindowSize.js
import { ref, onMounted, onUnmounted } from 'vue'
export function useWindowSize() {
const width = ref(window.innerWidth)
const height = ref(window.innerHeight)
const updateSize = () => {
width.value = window.innerWidth
height.value = window.innerHeight
}
// Event-Listener hinzufügen
onMounted(() => {
window.addEventListener('resize', updateSize)
})
// Event-Listener entfernen (Cleanup)
onUnmounted(() => {
window.removeEventListener('resize', updateSize)
})
return {
width,
height
}
}Verwendung:
vue
<script setup>
import { useWindowSize } from '../composables/useWindowSize.js'
const { width, height } = useWindowSize()
</script>
<template>
<div>
<h2>Fenstergröße</h2>
<p>Breite: {{ width }}px</p>
<p>Höhe: {{ height }}px</p>
<div
:style="{
width: '100%',
height: '50px',
backgroundColor: width > 768 ? 'lightgreen' : 'lightcoral'
}"
>
{{ width > 768 ? 'Desktop' : 'Mobile' }}
</div>
</div>
</template>✅ Zusammenfassung
In diesem Kapitel hast du gelernt:
- ✅ Was benutzerdefinierte Composables sind
- ✅ Einfaches Composable erstellen (
useCounter.js) - ✅ Composable mit Parameter (
useFetch.js) - ✅ Composables kombinieren
- ✅ Best Practices für Composables
- ✅ Praxis:
useWindowSize.jsComposable
🎯 Nächster Schritt: In Kapitel 26 lernst du Template Rendering & Event-Handling!
← Zurück zu Kapitel 24: Lebenszyklus-HooksWeiter zu Kapitel 26: Template & Events →
