Appearance
Kapitel 23: Composition API (Teil 3) - computed() & watch()
📙 Lernziel: computed() und watch() in Composition API meistern!
23.1 computed() in Composition API
computed() in Composition API ist einfacher als Options API.
Syntax:
javascript
import { computed } from 'vue'
const berechneteEigenschaft = computed(() => {
// Logik
return ergebnis
})Beispiel:
vue
<script setup>
import { ref, computed } from 'vue'
const items = ref([
{ id: 1, text: 'Lernen', done: false },
{ id: 2, text: 'Sport', done: true },
{ id: 3, text: 'Einkaufen', done: false }
])
// computed() - Anzahl erledigt
const doneCount = computed(() => {
return items.value.filter(item => item.done).length
})
// computed() - Anzahl offen
const undoneCount = computed(() => {
return items.value.filter(item => !item.done).length
})
// computed() - Alle erledigt?
const allDone = computed(() => {
return items.value.length > 0 && doneCount.value === items.value.length
})
</script>
<template>
<div>
<h2>To-Do Liste</h2>
<p>Gesamt: {{ items.length }}</p>
<p>Erledigt: {{ doneCount }}</p>
<p>Offen: {{ undoneCount }}</p>
<p>Alle erledigt? {{ allDone ? 'Ja!' : 'Nein' }}</p>
<ul>
<li v-for="item in items" :key="item.id">
<input type="checkbox" v-model="item.done" />
<span :style="{ textDecoration: item.done ? 'line-through' : 'none' }">
{{ item.text }}
</span>
</li>
</ul>
</div>
</template>Wichtig:
- ✅
computed()gibt ein readonly ref zurück! - ✅ Nur im
<script>mit.valuezugreifen - ✅ Im
<template>direkt nutzen (ohne.value)
23.2 computed() mit Setter
computed() mit Setter (selten nötig):
vue
<script setup>
import { ref, computed } from 'vue'
const firstName = ref('Max')
const lastName = ref('Mustermann')
// computed() mit Getter und Setter
const fullName = computed({
// Getter
get() {
return firstName.value + ' ' + lastName.value
},
// Setter
set(newValue) {
const names = newValue.split(' ')
firstName.value = names[0]
lastName.value = names[names.length - 1]
}
})
</script>
<template>
<div>
<p>Vorname: <input v-model="firstName" /></p>
<p>Nachname: <input v-model="lastName" /></p>
<p>Vollständiger Name: <input v-model="fullName" /></p>
</div>
</template>23.3 watch() in Composition API
watch() in Composition API ist flexibler.
Syntax:
javascript
import { watch } from 'vue'
watch(quelle, callback, optionen)Beispiel (einzelne Quelle):
vue
<script setup>
import { ref, watch } from 'vue'
const count = ref(0)
const message = ref('Hallo')
// watch() - Überwacht count
watch(count, (newValue, oldValue) => {
console.log(`count geändert: ${oldValue} → ${newValue}`)
})
// watch() - Überwacht message
watch(message, (newValue, oldValue) => {
console.log(`message geändert: ${oldValue} → ${newValue}`)
})
</script>
<template>
<div>
<p>Zähler: {{ count }}</p>
<button @click="count++">Erhöhen</button>
<p>Nachricht: {{ message }}</p>
<button @click="message = 'Vue 3 ist toll!'">Nachricht ändern</button>
</div>
</template>23.4 watch() für mehrere Quellen
Mehrere Quellen gleichzeitig überwachen:
vue
<script setup>
import { ref, watch } from 'vue'
const firstName = ref('')
const lastName = ref('')
// Array von Quellen
watch([firstName, lastName], (newValues, oldValues) => {
console.log('Vorname oder Nachname geändert!')
console.log('Neu:', newValues)
console.log('Alt:', oldValues)
})
</script>
<template>
<div>
<label>Vorname: <input v-model="firstName" /></label><br>
<label>Nachname: <input v-model="lastName" /></label><br>
<p>Vollständiger Name: {{ firstName }} {{ lastName }}</p>
</div>
</template>23.5 watch() für Objekte (Tiefenüberwachung)
Problem: watch() überwacht standardmäßig nur Referenzänderungen.
Lösung: deep: true Option
vue
<script setup>
import { reactive, watch } from 'vue'
const user = reactive({
name: 'Max',
address: {
city: 'Berlin',
street: 'Hauptstraße'
}
})
// ❌ Funktioniert nicht für tiefe Änderungen
watch(user, () => {
console.log('user geändert')
})
// ✅ Mit deep: true
watch(user, () => {
console.log('user (tief) geändert')
}, { deep: true })
// ✅ Bessere Lösung: Nur spezifisches Property überwachen
watch(() => user.address.city, (newCity) => {
console.log('Stadt geändert:', newCity)
})
</script>
<template>
<div>
<p>Name: {{ user.name }}</p>
<p>Stadt: {{ user.address.city }}</p>
<button @click="user.address.city = 'München'">Stadt ändern</button>
</div>
</template>Empfehlung:
- Nutze
() => user.address.citystattdeep: true(effizienter)!
23.6 watchEffect() in Composition API
watchEffect() führt eine Funktion sofort aus und überwacht alle darin verwendeten reaktiven Daten.
Beispiel:
vue
<script setup>
import { ref, watchEffect } from 'vue'
const count = ref(0)
const message = ref('Hallo')
// watchEffect() - Führt sofort aus und überwacht Abhängigkeiten
watchEffect(() => {
console.log(`count: ${count.value}, message: ${message.value}`)
})
// count erhöhen
const increment = () => {
count.value++
}
// message ändern
const changeMessage = () => {
message.value = 'Vue 3 ist toll!'
}
</script>
<template>
<div>
<p>Zähler: {{ count }}</p>
<button @click="increment">Erhöhen</button>
<p>Nachricht: {{ message }}</p>
<button @click="changeMessage">Nachricht ändern</button>
</div>
</template>23.7 watch() vs watchEffect() - Wann was nutzen?
watch() nutzen wenn:
- ✅ Du brauchst
oldValueundnewValue - ✅ Du willst die Quelle explizit angeben
- ✅ Du willst nicht sofort ausführen (nur bei Änderung)
watchEffect() nutzen wenn:
- ✅ Du willst sofort ausführen
- ✅ Du hast mehrere Abhängigkeiten (automatische Erkennung)
- ✅ Du brauchst keine
oldValue
Beispiel - API Aufruf:
vue
<script setup>
import { ref, watch } from 'vue'
const userId = ref(1)
const userData = ref(null)
// watch() - API Aufruf nur wenn userId sich ändert
watch(userId, async (newId) => {
const response = await fetch(`https://jsonplaceholder.typicode.com/users/${newId}`)
userData.value = await response.json()
}, { immediate: true }) // Sofort ausführen
</script>
<template>
<div>
<button @click="userId++">Nächster User</button>
<div v-if="userData">
<h2>{{ userData.name }}</h2>
<p>Email: {{ userData.email }}</p>
</div>
</div>
</template>23.8 Übung: Suchfeld mit watch() (Debouncing)
Aufgabe: Erstelle ein Suchfeld, das bei Eingabe eine API abfragt (Debouncing).
Lösung:
vue
<script setup>
import { ref, watch } from 'vue'
const searchQuery = ref('')
const results = ref([])
const isLoading = ref(false)
let timeoutId = null
// watch() mit Debouncing
watch(searchQuery, (newQuery) => {
if (newQuery.trim() === '') {
results.value = []
return
}
// Debouncing: Warte 500ms vor API Aufruf
clearTimeout(timeoutId)
timeoutId = setTimeout(async () => {
isLoading.value = true
try {
const response = await fetch(`https://api.example.com/search?q=${newQuery}`)
results.value = await response.json()
} catch (error) {
console.error('Fehler:', error)
} finally {
isLoading.value = false
}
}, 500)
})
</script>
<template>
<div>
<input
v-model="searchQuery"
placeholder="Suchen..."
/>
<div v-if="isLoading">Lädt...</div>
<ul v-else>
<li v-for="result in results" :key="result.id">
{{ result.name }}
</li>
</ul>
</div>
</template>✅ Zusammenfassung
In diesem Kapitel hast du gelernt:
- ✅
computed()in Composition API nutzen - ✅
computed()mit Setter - ✅
watch()in Composition API nutzen - ✅
watch()für mehrere Quellen - ✅
watch()für Objekte (deep: true) - ✅
watchEffect()in Composition API nutzen - ✅ Unterschied zwischen
watch()undwatchEffect() - ✅ Praxis: Suchfeld mit Debouncing
🎯 Nächster Schritt: In Kapitel 24 lernst du Lebenszyklus-Hooks in Composition API!
← Zurück zu Kapitel 22: Reaktive GrundlagenWeiter zu Kapitel 24: Lebenszyklus-Hooks →
