[Medium] watch vs watchEffect
1. Cosa sono watch e watchEffect?
Cosa sono
watchewatchEffect?
watch e watchEffect sono API di Vue 3 per reagire ai cambiamenti nello stato reattivo.
watch
Definizione: osserva esplicitamente una o più sorgenti e esegue una callback quando cambiano.
<script setup>
import { ref, watch } from 'vue';
const count = ref(0);
const message = ref('Hello');
// osservare una singola sorgente
watch(count, (newValue, oldValue) => {
console.log(`count cambiato da ${oldValue} a ${newValue}`);
});
// osservare sorgenti multiple
watch([count, message], ([newCount, newMessage], [oldCount, oldMessage]) => {
console.log('count o message cambiato');
});
</script>
watchEffect
Definizione: viene eseguito immediatamente e traccia automaticamente le dipendenze reattive utilizzate all'interno della sua callback.
<script setup>
import { ref, watchEffect } from 'vue';
const count = ref(0);
const message = ref('Hello');
// traccia automaticamente count e message
watchEffect(() => {
console.log(`count: ${count.value}, message: ${message.value}`);
// si ri-esegue quando count/message cambia
});
</script>
2. watch vs watchEffect: Differenze Principali
Differenze principali tra
watchewatchEffect
1. Dichiarazione della sorgente
watch: sorgente/i esplicita/e.
const count = ref(0);
const message = ref('Hello');
watch(count, (newVal, oldVal) => {
console.log('count cambiato');
});
watch([count, message], ([newCount, newMessage]) => {
console.log('count o message cambiato');
});
watchEffect: tracciamento implicito delle dipendenze.
const count = ref(0);
const message = ref('Hello');
watchEffect(() => {
console.log(count.value); // tracciato automaticamente
console.log(message.value); // tracciato automaticamente
});
2. Tempistica di esecuzione
watch: lazy di default; viene eseguito solo dopo il cambiamento della sorgente.
const count = ref(0);
watch(count, (newVal) => {
console.log('eseguito');
});
count.value = 1; // attiva la callback
watchEffect: viene eseguito immediatamente, poi si ri-esegue agli aggiornamenti delle dipendenze.
const count = ref(0);
watchEffect(() => {
console.log('eseguito'); // prima esecuzione immediata
console.log(count.value);
});
count.value = 1; // si esegue di nuovo
3. Accesso al valore precedente
watch: fornisce newValue e oldValue.
const count = ref(0);
watch(count, (newVal, oldVal) => {
console.log(`da ${oldVal} a ${newVal}`);
});
watchEffect: nessun valore precedente diretto.
const count = ref(0);
watchEffect(() => {
console.log(count.value); // solo valore corrente
});
4. Fermare i watcher
Entrambi restituiscono una funzione di stop.
const stopWatch = watch(count, (newVal) => {
console.log(newVal);
});
const stopEffect = watchEffect(() => {
console.log(count.value);
});
stopWatch();
stopEffect();
3. Quando usare watch vs watchEffect?
Quando dovresti scegliere ciascuna API?
Usa watch quando
- Hai bisogno di sorgenti esplicite.
watch(userId, (newId) => {
fetchUser(newId);
});
- Hai bisogno del valore precedente.
watch(count, (newVal, oldVal) => {
console.log(`da ${oldVal} a ${newVal}`);
});
- Hai bisogno di esecuzione lazy.
watch(searchQuery, (newQuery) => {
if (newQuery.length > 2) {
search(newQuery);
}
});
- Hai bisogno di controllo a grana fine (
immediate,deep, ecc.).
watch(
() => user.value.id,
(newId) => {
fetchUser(newId);
},
{ immediate: true, deep: true }
);
Usa watchEffect quando
- Vuoi il tracciamento automatico delle dipendenze.
watchEffect(() => {
if (user.value && permissions.value.includes('admin')) {
loadAdminData();
}
});
- Non hai bisogno del valore precedente.
watchEffect(() => {
console.log(`conteggio corrente: ${count.value}`);
});
- Vuoi la prima esecuzione immediata.
watchEffect(() => {
updateChart(count.value, message.value);
});
4. Domande Comuni nei Colloqui
Domande comuni nei colloqui
Domanda 1: ordine di esecuzione
Spiega output e ordine:
const count = ref(0);
const message = ref('Hello');
watch(count, (newVal) => {
console.log('watch:', newVal);
});
watchEffect(() => {
console.log('watchEffect:', count.value, message.value);
});
count.value = 1;
message.value = 'World';
Clicca per vedere la risposta
watch è lazy (nessuna esecuzione immediata), ma watchEffect viene eseguito immediatamente.
Sequenza attesa:
watchEffect: 0 Hello(esecuzione iniziale)watch: 1(count cambiato)watchEffect: 1 Hello(count cambiato)watchEffect: 1 World(message cambiato)
Punti chiave:
watchreagisce solo alla sorgente osservata esplicitamentewatchEffectreagisce a qualsiasi dipendenza reattiva utilizzata nella callback
Domanda 2: valore precedente con watchEffect
Come si accede al valore precedente usando watchEffect?
Clicca per vedere la risposta
watchEffect non fornisce direttamente il valore precedente.
Opzione 1: mantenere il proprio ref precedente
const count = ref(0);
const prevCount = ref(0);
watchEffect(() => {
console.log(`da ${prevCount.value} a ${count.value}`);
prevCount.value = count.value;
});
Opzione 2: usare watch
watch(count, (newVal, oldVal) => {
console.log(`da ${oldVal} a ${newVal}`);
});
Raccomandazione: se il valore precedente è necessario, preferire watch.
Domanda 3: scegliere watch o watchEffect
Scegli l'API per ogni scenario:
// Scenario 1: ricaricare dati utente quando userId cambia
const userId = ref(1);
// Scenario 2: abilitare invio quando il form è valido
const form = reactive({ username: '', password: '' });
const isValid = computed(() => form.username && form.password);
// Scenario 3: ricerca con debounce al cambio di keyword
const searchQuery = ref('');
Clicca per vedere la risposta
Scenario 1: cambio userId -> watch
watch(userId, (newId) => {
fetchUser(newId);
});
Scenario 2: effetto collaterale validità form -> watchEffect
watchEffect(() => {
submitButton.disabled = !isValid.value;
});
Scenario 3: ricerca con debounce -> watch
let timeoutId;
watch(searchQuery, (newQuery) => {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => {
search(newQuery);
}, 300);
});
Regola di scelta:
- sorgente esplicita / valore precedente / opzioni di controllo ->
watch - tracciamento automatico delle dipendenze + esecuzione immediata ->
watchEffect
5. Best Practices (Buone Pratiche)
Buone pratiche
Raccomandato
// 1) sorgente esplicita -> watch
watch(userId, (newId) => {
fetchUser(newId);
});
// 2) tracciamento automatico di dipendenze multiple -> watchEffect
watchEffect(() => {
if (user.value && permissions.value.includes('admin')) {
loadAdminData();
}
});
// 3) valore precedente necessario -> watch
watch(count, (newVal, oldVal) => {
console.log(`da ${oldVal} a ${newVal}`);
});
// 4) pulizia
onUnmounted(() => {
stopWatch();
stopEffect();
});
Da evitare
// 1) evitare effetti collaterali asincroni non gestiti in watchEffect
watchEffect(async () => {
const data = await fetchData();
// potenziale race condition/leak se non gestito
});
// 2) evitare l'uso eccessivo di watchEffect
watchEffect(() => {
console.log(count.value); // watch(count, ...) potrebbe essere più chiaro
});
// 3) evitare di mutare la sorgente tracciata nello stesso effetto (rischio loop)
watchEffect(() => {
count.value++; // potrebbe causare un loop infinito
});
6. Riepilogo per i Colloqui
Riepilogo per i colloqui
Promemoria rapido
watch:
- dichiarazione esplicita della sorgente
- lazy di default
- valore precedente disponibile
- migliore per scenari controllati
watchEffect:
- tracciamento automatico delle dipendenze
- esecuzione immediata
- nessun valore precedente
- migliore per effetti collaterali reattivi concisi
Regola pratica:
- controllo esplicito ->
watch - tracciamento automatico ->
watchEffect - valore precedente necessario ->
watch - esecuzione iniziale immediata ->
watchEffect
Risposta esempio
D: Qual è la differenza tra watch e watchEffect?
Entrambi reagiscono ai cambiamenti reattivi in Vue 3.
watchtraccia sorgenti dichiarate esplicitamente e fornisce valori vecchi/nuovi; è lazy di default.watchEffectviene eseguito immediatamente e traccia automaticamente le dipendenze utilizzate all'interno della callback, ma non fornisce il valore precedente. Usarewatchper precisione e controllo; usarewatchEffectper la raccolta automatica delle dipendenze.
D: Quando dovrei usare ciascuno?
Usare
watchquando servono controllo esplicito della sorgente, valori precedenti, o opzioni come debounce/deep/immediate. UsarewatchEffectquando si vuole un'esecuzione immediata e tracciamento automatico tra molteplici valori reattivi correlati.