watch und Suchfunktionalität
In diesem Kapitel lernst du, watch und watchEffect zu verwenden, um auf Datenänderungen zu hören und eine Echtzeitfilterung im Blog-Suchfeld zu implementieren.
Welche Szenarien benötigen watch?
In den meisten Fällen ist Vues reaktive Bindung ausreichend: Wenn sich Daten ändern, aktualisiert sich die Vorlage automatisch.
Aber einige Szenarien erfordern, dass duproaktiv ein Stück Logik ausführst, wenn sich Daten ändern, wie zum Beispiel:
- Nach der Eingabe in das Suchfeld die Artikelliste filtern
- Wenn der Benutzer die Kategorien wechselt, den Seitentitel aktualisieren
- Nach Datenänderungen in localStorage speichern
- Wenn sich Formularfelder ändern, Formularvalidierung durchführen
Dann brauchst duwatchundwatchEffect。
watch — präzise Überwachung
watchHöre auf angegebene Datenquellen und führe eine Callback-Funktion aus, wenn sich Daten ändern.
Du kannst denneuen Wertund denalten Wertabrufen, um sie zu vergleichen.
Beispiel
import { ref, watch } from 'vue'
const keyword = ref('')
// watch(die überwachte Datenquelle, Callback-Funktion)
watch(keyword, (newVal, oldVal) => {
console.log(`Der Suchbegriff hat sich von "${oldVal}" zu "${newVal}" geändert`)
// Filterlogik hier ausführen
})
</script>
<template>
<input v-model="keyword" placeholder="Artikel suchen...">
<p>Aktuelle Suche: {{ keyword }}</p>
</template>
Drei Datenquellen für watch
| Datenquellentyp | Syntax | Beispiel |
|---|---|---|
| Einzelne ref | watch(ref, callback) | watch(keyword, fn) |
| Eigenschaft eines reaktiven Objekts | watch(() => obj.prop, callback) | watch(() => user.name, fn) |
| Mehrere Datenquellen | watch([ref1, ref2], callback) | watch([kw, cat], fn) |
Beispiel
const keyword = ref('')
const category = ref('All')
// Eine einzelne ref überwachen
watch(keyword, (newVal) => {
document.title = newVal ?`Search: ${newVal}` : 'EXAMPLE Blog'
})
// Mehrere Datenquellen überwachen
watch([keyword, category], ([newKw, newCat], [oldKw, oldCat]) => {
console.log('Keyword oder Kategorie geändert, Artikel werden neu gefiltert')
// Filterlogik ausführen...
})
Standardmäßig wird watchverzögert ausgeführt—es wird nicht ausgelöst, wenn Daten zum ersten Mal einen Wert haben, sondern nur bei nachfolgenden Änderungen. Wenn du es einmal während der Initialisierung ausführen möchtest, füge die
{ immediate: true }Konfigurationsoption hinzu.
watchEffect — Verfolgt automatisch Abhängigkeiten
watchEffectDatenquellen müssen nicht manuell angegeben werden; es verfolgt automatisch die im Callback verwendeten reaktiven Daten.
Der größte Unterschied zu watch:watchEffect wird sofort einmal ausgeführt。
Beispiel
import { ref, watchEffect } from 'vue'
const keyword = ref('')
const articles = ref([
{ id: 1, title: 'Vue3-Einführung' },
{ id: 2, title: 'JS Async' },
])
const filteredArticles = ref([])
// watchEffect verfolgt keyword und articles automatisch
// Und es wird sofort einmal ausgeführt, um filteredArticles zu initialisieren
watchEffect(() => {
// Alle in der Funktion verwendeten reaktiven Daten werden automatisch erfasst
filteredArticles.value = articles.value.filter(
a => a.title.includes(keyword.value)
)
console.log(`Filterergebnis: ${filteredArticles.value.length} Artikel`)
})
</script>
<template>
<input v-model="keyword" placeholder="Suchen">
<div v-for="a in filteredArticles" :key="a.id">
{{ a.title }}
</div>
</template>
watch vs watchEffect Vergleich
| Merkmal | watch | watchEffect |
|---|---|---|
| Ob die Datenquelle angegeben ist | Manuell angegeben | Verfolgt Abhängigkeiten automatisch |
| Erste Ausführung | Wird standardmäßig nicht ausgeführt (konfigurierbar mit immediate) | Wird sofort ausgeführt |
| Abrufen des alten Werts | Kann den alten Wert abrufen | Kann den alten Wert nicht abrufen |
| Anwendbare Szenarien | Wenn Sie alte und neue Werte vergleichen und den Zeitpunkt präzise steuern müssen | Wenn Ihnen alte/neue Werte egal sind und Sie nur auf Änderungen reagieren möchten |
Einfache Auswahlhilfe: Wenn Ihre Logik lautet: „Wenn sich X ändert, tue Y“, verwenden Sie watch. Wenn Ihre Logik lautet: „Sobald sich irgendeine Abhängigkeit ändert, wird das Ergebnis neu berechnet“, verwenden Sie watchEffect oder computed. In der Praxis sollte bei Daten, die berechnet werden können, zuerst computed verwendet werden; watch dient der Behandlung von „Seiteneffekten“ – wie dem Aufrufen von APIs, dem lokalen Speichern oder der Manipulation des DOM.
Debounce-Ansatz
Jeder Tastendruck im Suchfeld löst eine Filterung aus. Für lokale Daten ist das in Ordnung, aber wenn jede Eingabe eine Netzwerkanfrage sendet, wird viel verschwendet.
DebounceDie Idee: Warten, bis der Benutzer eine Zeit lang nicht mehr tippt, und dann die Operation ausführen.
Beispiel
const keyword = ref('')
const filteredArticles = ref([])
let timer = null // Timer-ID
watch(keyword, (newVal) => {
// Den vorherigen Timer löschen
clearTimeout(timer)
// Filterung nur ausführen, wenn innerhalb von 300ms keine neue Eingabeänderung erfolgt
timer = setTimeout(() => {
filteredArticles.value = articles.value.filter(
a => a.title.includes(newVal) || a.summary.includes(newVal)
)
}, 300)
})
Für dieses Blogprojekt sind die Daten lokal, daher ist Debouncing nicht notwendig. Aber dieses Konzept zu verstehen, ist später für größere Projekte sehr hilfreich.
Praxis: Echtzeitsuche zum Blog hinzufügen
Füge eine Suchbox zur NavBar-Komponente hinzu, um Artikeltitel und Zusammenfassungen in Echtzeit nach Stichwort zu filtern.
Beispiel
<script setup>
import { ref, computed, onMounted } from 'vue'
import BlogCard from '../components/BlogCard.vue'
import CategoryFilter from '../components/CategoryFilter.vue'
const articles = ref([]) // Alle Artikel
const isLoading = ref(true)
const activeCategory = ref('All')
const keyword = ref('') // Suchbegriff
// Alle Kategorien extrahieren
const categories = computed(() => {
const cats = articles.value.map(a => a.category)
return ['Alle', ...new Set(cats)]
})
// computed verfolgt automatisch Änderungen an keyword und activeCategory
// Wenn sich eines davon ändert, wird das Filterergebnis automatisch neu berechnet
const filteredArticles = computed(() => {
let result = articles.value
// Schritt 1: Nach Kategorie filtern
if (activeCategory.value !== 'All') {
result = result.filter(a => a.category === activeCategory.value)
}
// Schritt 2: Nach Suchbegriff filtern (Titel und Zusammenfassung durchsuchen)
if (keyword.value.trim()) {
const kw = keyword.value.trim().toLowerCase()
result = result.filter(a =>
a.title.toLowerCase().includes(kw) ||
a.summary.toLowerCase().includes(kw)
)
}
return result
})
// Das Wechseln von Kategorien löscht die Suche nicht; die beiden funktionieren in Kombination
function handleCategoryChange(cat) {
activeCategory.value = cat
}
// Daten laden
onMounted(async () => {
try {
const res = await fetch('/posts.json')
articles.value = await res.json()
} catch (err) {
console.error('Laden fehlgeschlagen', err)
} finally {
isLoading.value = false
}
})
</script>
<template>
<div class="home">
<!-- Suchfeld -->
<div class="search-bar">
<input
v-model="keyword"
type="text"
placeholder="Titel oder Zusammenfassung des Artikels suchen..."
class="search-input"
/>
<span v-if="keyword" class="clear-btn" @click="keyword = ''">✕</span>
</div>
<!-- Kategoriefilter -->
<CategoryFilter
:="categories"
active-category="activeCategory"
@update-category="handleCategoryChange"
/>
<!-- Laden / Leer / Normal -->
<p v-if="isLoading">Wird geladen...</p>
<p v-else-if="filteredArticles.length === 0">Keine passenden Artikel gefunden</p>
<div v-else class="article-grid">
<BlogCard
v-for="article in filteredArticles"
:key="article.id"
:id="article.id"
:title="article.title"
:summary="article.summary"
:date="article.date"
:category="article.category"
/>
</div>
</div>
</template>
<style scoped>
.home { max-width: 960px; margin: 0 auto; padding: 20px; }
.search-bar {
position: relative;
margin-bottom: 20px;
}
.search-input {
width: 100%;
padding: 12px 40px 12px 16px;
border: 2px solid #eee;
border-radius: 8px;
font-size: 15px;
outline: none;
transition: border-color 0.2s;
}
.search-input:focus {
border-color: #427DB8;
}
.clear-btn {
position: absolute;
right: 12px;
top: 50%;
transform: translateY(-50%);
cursor: pointer;
color: #999;
font-size: 18px;
}
.article-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
gap: 24px;
margin-top: 20px;
}
</style>
Jetzt arbeiten Suche und Kategoriefilterung zusammen – nach Eingabe eines Suchbegriffs werden beim Klicken auf eine Kategorie-Schaltfläche die Filter gestapelt. Beide Bedingungen sind reaktiv; wenn sich eine von beiden ändert, wird filteredArticles automatisch aktualisiert.
Kapitelzusammenfassung
In diesem Kapitel hast du watch gemeistert (Datenquelle manuell angeben, alte und neue Werte abrufen) sowie watchEffect (verfolgt Abhängigkeiten automatisch, wird sofort ausgeführt) und gelernt, computed für die kombinierte Suche + Kategoriefilterung zu verwenden.
Das Echtzeit-Filtererlebnis eines Suchfelds ist viel flüssiger als "tippen und auf eine Schaltfläche klicken"—das ist der Wert eines reaktiven Systems.
Andere Erweiterungen