Vue.js: Observadores: watch
Última atualização: 2026-08-26
Os listeners (watch / watchEffect) permitem que você monitore alterações em dados reativos e execute efeitos colaterais quando essas alterações ocorrem. computed é usado para “derivar novos valores”, enquanto watch é usado para “acionar ações” (como fazer solicitações ou definir o estado).
watch é um componente essencial do sistema de reatividade do Vue, com três principais casos de uso: acionar chamadas de API quando os dados mudam, supressão de rebote em entradas de formulário e sincronização entre dados.
1. O que você vai aprender
watch()Sintaxe básica e 4 parâmetros (fonte / callback / opções / deep)- Fique atento a quatro tipos de fontes de dados: ref, reativas, calculadas e funções getter
watchEffect()A diferença entre o rastreamento automático de dependências ewatchimmediate: trueOpção de acionamento imediatodeep: trueMonitoramento detalhado das propriedades internas- Implementação do controle de aceleração anti-vibração
- Efeitos colaterais de limpeza (onCleanup / descarregamento do componente)
2. Problemas de desempenho da API com uma caixa de pesquisa
(2) Problema: uma solicitação é enviada a cada tecla pressionada, fazendo com que a API trave
Alice implementou uma caixa de pesquisa de produtos no painel de administração:
// ❌ The "Broken" Version:A request is sent for each character
import { ref } from 'vue'
const searchQuery = ref('')
watch(searchQuery, async (newValue) => {
// A request is sent with every input
const results = await fetch(`/api/search?q=${newValue}`)
searchResults.value = await results.json()
})
O servidor da API travou:
- O usuário digita “iPhone 15” (8 caracteres) = 8 chamadas de API
- 100 usuários digitando simultaneamente = 800 chamadas de API
- Limite de chamadas da API: 100/min → o servidor trava
O desenvolvedor de backend Charlie relata:
“Alice, sua busca está sobrecarregando a API. Precisamos aplicar um debounce. Espere 300 ms depois que o usuário parar de digitar antes de enviar a solicitação.”
(2) Solução para visualização no relógio + estabilização de imagem
import { ref, watch } from 'vue'
const searchQuery = ref('')
const searchResults = ref([])
// Image Stabilization:300ms A リクエスト is only sent if there are no internal changes.
let timeoutId = null
watch(searchQuery, (newValue) => {
if (timeoutId) clearTimeout(timeoutId)
timeoutId = setTimeout(async () => {
const response = await fetch(`/api/search?q=${newValue}`)
searchResults.value = await response.json()
}, 300)
})
(3) Receita
Após implementar o debouncing:
- Chamadas de API: 8 por pesquisa → 1 por pesquisa (-87%)
- Carga do servidor: 800 → 100 (-87%)
- Tempo de resposta: 50 ms (em cache) x 500 ms (em tempo real)
- Tempo de atividade do servidor: 99,9% (sem mais falhas)
3. Sintaxe básica do watch()
(1) 4 parâmetros
import { ref, watch } from 'vue'
const count = ref(0)
watch(
// 1. Monitoring Source:Reactive data to be monitored
count,
// 2. Callback function:(New Value, Old value) => {...}
(newVal, oldVal) => {
console.log(`count: ${oldVal} → ${newVal}`)
},
// 3. Options: immediate, deep, flush
{ immediate: true, deep: true }
)
(2) Preste atenção ao árbitro
const count = ref(0)
// Monitoring ref
watch(count, (newVal, oldVal) => {
console.log(`count: ${oldVal} → ${newVal}`)
})
(3) Monitoramento de várias referências
const firstName = ref('Alice')
const lastName = ref('Smith')
// Monitor multiple
watch([firstName, lastName], ([newFirst, newLast], [oldFirst, oldLast]) => {
console.log(`Name: ${newFirst} ${newLast}`)
})
4. 4 Tipos de fontes de áudio
(1) Preste atenção ao árbitro
const count = ref(0)
watch(count, (newVal, oldVal) => {
console.log(`count: ${oldVal} → ${newVal}`)
})
(2) Escutando objetos reativos
import { reactive, watch } from 'vue'
const user = reactive({ name: 'Alice', age: 25 })
// Monitor the entire reactive(Automatic Depth)
watch(user, (newVal, oldVal) => {
console.log('User changed:', newVal)
})
// ✅ Modifying internal properties also triggers
user.name = 'Bob' // Trigger
(3) Funções getter de escuta
const state = reactive({ user: { profile: { name: 'Alice' } } })
// Monitor only specific properties(Precise Control)
watch(
() => state.user.profile.name,
(newVal) => {
console.log(`Name: ${newVal}`)
}
)
(4) Fique atento ao computed
const count = ref(0)
const doubleCount = computed(() => count.value * 2)
watch(doubleCount, (newVal) => {
console.log(`Double: ${newVal}`)
})
(4) Comparação entre quatro tipos de fontes de monitoramento
| Fonte dos dados | Notação | Análise aprofundada | Aplicável |
|---|---|---|---|
| ref | watch(ref, fn) |
Luz padrão | Referência única |
| reativo | watch(reactive, fn) |
Aprendizado Profundo Automático | Objeto |
| getter | watch(() => obj.x, fn) |
padrão superficial | propriedade específica |
| calculado | watch(computed, fn) |
Luz padrão | Valor derivado |
5. watchEffect(): Rastreamento automático de dependências
(1) Sintaxe básica
import { ref, watchEffect } from 'vue'
const count = ref(0)
const name = ref('Alice')
// You don't need to specify who to listen for.,Reactive data used within the function body is automatically tracked
watchEffect(() => {
console.log(`count: ${count.value}, name: ${name.value}`)
// count Re-execute when changes occur
// name Re-execute when changes occur
})
(2) watchEffect x watch
| Aspecto | relógio | efeito do relógio |
|---|---|---|
| Fonte de áudio | Especificada manualmente | Rastreamento automático |
| Executar agora | Padrão: Não (requer immediate: true) |
✅ Sim |
| Valores antigos e novos | ✅ Ambos | ❌ Apenas o novo valor |
| Efeitos colaterais da limpeza | onCleanup | onCleanup |
| Casos de uso | Monitoramento preciso | Efeitos colaterais gerais |
(3) Limpeza do watchEffect
import { watchEffect, onCleanup } from 'vue'
watchEffect((onCleanup) => {
// Set a Timer, Subscribe, Event Listening
const timer = setInterval(() => {
console.log('tick')
}, 1000)
// Cleanup:Called before the next execution or when the component is unloaded
onCleanup(() => {
clearInterval(timer)
})
})
6. Opções: imediata / profunda / completa
(1) imediato: acionado imediatamente
const count = ref(0)
// Default:count Change is what triggers it
watch(count, (newVal) => console.log(newVal)) // Will not be executed immediately
// immediate: true: Trigger Immediately 1 time
watch(count, (newVal) => console.log(newVal), { immediate: true })
// ✅ Execute Now:0 → 0
(2) deep: Monitoramento aprofundado
import { ref, watch } from 'vue'
const user = ref({ profile: { name: 'Alice' } })
// Default:ref The object being wrapped .value It is triggered only when the entire replacement is made
watch(user, () => console.log('changed')) // ❌ user.value.profile.name = 'Bob' Does not trigger
// deep: true:Changes to internal properties also trigger
watch(user, () => console.log('changed'), { deep: true })
// ✅ user.value.profile.name = 'Bob' Trigger
(3) flush: Quando é acionado
// Default 'pre':Triggered before the component is updated
watch(source, fn, { flush: 'pre' })
// 'post':Triggered after a component update(DOM Updated)
watch(source, fn, { flush: 'post' })
// 'sync':Synchronous Triggering(Not commonly used,Poor performance)
watch(source, fn, { flush: 'sync' })
7. Exemplo completo: Caixa de pesquisa + supressão de cliques repetidos
▶ Exemplo: 1. Relógio básico para um árbitro
<template>
<div>
<input v-model="count">
<p>Count: {{ count }}</p>
</div>
</template>
<script setup>
import { ref, watch } from 'vue'
const count = ref(0)
watch(count, (newVal, oldVal) => {
console.log(`Count changed: ${oldVal} → ${newVal}`)
})
</script>
▶ Exemplo: 2. Supressão de rebote na pesquisa
<template>
<div>
<input v-model="searchQuery" placeholder="Search products...">
<p>Results: {{ results.length }} items</p>
</div>
</template>
<script setup>
import { ref, watch } from 'vue'
const searchQuery = ref('')
const results = ref([])
let timeoutId = null
watch(searchQuery, (newQuery) => {
// Clear the previous timer
if (timeoutId) clearTimeout(timeoutId)
// Set a new timer:300ms Send the request afterward
timeoutId = setTimeout(async () => {
if (newQuery.length === 0) {
results.value = []
return
}
const res = await fetch(`/api/search?q=${newQuery}`)
results.value = await res.json()
}, 300)
})
</script>
▶ Exemplo: 3. Observando mudanças na profundidade reativa
import { reactive, watch } from 'vue'
const user = reactive({
profile: { name: 'Alice', age: 25 },
settings: { theme: 'dark' }
})
// Monitor the entire user(Automatic Depth)
watch(user, (newVal) => {
console.log('User changed:', JSON.stringify(newVal))
})
// ✅ Modifying any property triggers
user.profile.name = 'Bob' // Trigger
user.settings.theme = 'light' // Trigger
▶ Exemplo: 4. Rastreamento automático com watchEffect
import { ref, watchEffect } from 'vue'
const count = ref(0)
const name = ref('Alice')
// Do not specify a source to listen to,Automatic tracking used in the function body
watchEffect(() => {
console.log(`count: ${count.value}, name: ${name.value}`)
// count Re-execute when changes occur
// name Re-execute when changes occur
})
// Execute Now 1 time
count.value = 1 // Re-execute
name.value = 'Bob' // Re-execute
▶ Exemplo: 5. Implementação de debounce e throttling
import { ref, watch } from 'vue'
const searchQuery = ref('')
// Image Stabilization:300ms Last trigger inside
let debounceId = null
watch(searchQuery, (val) => {
if (debounceId) clearTimeout(debounceId)
debounceId = setTimeout(() => {
console.log('Search:', val)
}, 300)
})
// Cost-Cutting: Max 1 trigger per second
const scrollY = ref(0)
let lastRun = 0
watch(scrollY, (val) => {
const now = Date.now()
if (now - lastRun >= 1000) {
lastRun = now
console.log('Scroll:', val)
}
})
▶ Exemplo: 6. Referência rápida para 5 erros comuns
| Erro | Sintoma | Solução |
|---|---|---|
| Ouvir reações sem definir “deep” | Alterações nas propriedades internas não acionam | watch(obj, fn, { deep: true }) |
| Ouvir todo o objeto wrapper de referência | Alterações nas propriedades internas não acionam | Mudar para reativo ou adicionar “deep” |
| O watchEffect cria um loop infinito | o navegador trava | evite modificar os dados utilizados dentro da função |
| Temporizador não foi liberado | Vazamento de memória | Use onCleanup |
| Executar ofuscação imediatamente | Quer usar o valor antigo, mas não o obteve | Usar immediate + oldVal (pode estar indefinido) |
❓ Perguntas Frequentes
P: Devo usar
watchouwatchEffect? R: Usewatchsempre que possível (para controle preciso e acesso aos valores antigos e novos).watchEffecté adequado para situações em que você deseja executar uma ação quando um dado específico é alterado, mas não quer listar todas as dependências. Em termos de desempenho, eles são praticamente iguais, emborawatchEffectseja um pouco mais lento (já que precisa rastrear as dependências dentro do corpo da função).
P: Ao observar um
ref, quais são os tipos dos valores antigo e novo? R:.value. Se orefenvolver um objeto, tanto o valor antigo quanto o novo sãoref.value(as referências são as mesmas, a menos que o valor seja substituído por completo). Para monitorar alterações em propriedades internas, usereactiveou uma função getter.
P: Ao usar
watchpara observar um objeto wrapperref, como se observam suas propriedades internas? R: Existem três maneiras: (1)watch(ref, fn, { deep: true }); (2)watch(() => ref.value.x, fn)(getter); (3) Mudar para um wrapper reativo.
P: O
watchpode monitorar propriedades? R: Sim.watch(() => props.count, fn). Observe que as propriedades são somente para leitura; não as modifique na callbackwatch.
P: O que é
oldValquandoimmediate: true? R:undefined(Não há “valor anterior” no primeiro acionamento). Você pode recuperar o valor anterior a partir do valor inicial da referência ou do armazenamento.
P: O
watchpode ser assíncrono? R: A função de retorno de chamada pode serasync, mas owatchem si é acionado de forma síncrona. Coloque a lógica assíncrona dentro da função de retorno de chamada:(newVal) => { setTimeout(() => {...}, 0) }.
P: Quando o
onCleanupé acionado? R: Ele é acionado em dois momentos: (1) antes da reexecução de um watch (após a última limpeza); (2) quando o componente é desmontado. É adequado para limpar temporizadores, cancelar solicitações e remover ouvintes de eventos.
📖 Resumo
- watch: Monitora alterações em dados reativos; 4 parâmetros: source / callback / options / deep
- 4 tipos de observadores: ref / reativo / funções getter / computado
- O
watchEffectrastreia automaticamente as dependências reativas dentro do corpo da função e a executa uma única vez imediatamente - immediate: true — É acionado imediatamente; deep: true — Monitora detalhadamente as propriedades internas
- Anti-stuttering: executado somente se o último acionamento tiver ocorrido em um intervalo de 300 ms; Limitação: no máximo uma vez por segundo
- onCleanup: Limpar efeitos colaterais: temporizadores, assinaturas, ouvintes de eventos
- watch vs watchEffect: o watch oferece controle preciso e rastreia tanto os valores antigos quanto os novos; o watchEffect rastreia automaticamente as alterações e é mais simples
📝 Exercícios
-
Questões básicas (Dificuldade: ⭐)
Use
watchpara monitorar uma variávelrefcount:- Quando a contagem mudar, exiba “contagem: ${antiga} → ${nova}”
- Use
immediate: truepara executar a função de retorno de chamada imediatamente uma vez
-
Problemas avançados (Dificuldade: ⭐⭐)
Implemente um recurso de “salvamento automático” usando o watch:
- Editar um texto de referência
content - Chama automaticamente a função
save(content)após 1 segundo (supressão de rebote) - Zerar o cronômetro ao mudar para outra página (descarregamento do componente)
- Editar um texto de referência
-
Problema desafiador (Dificuldade: ⭐⭐⭐)
Implementar um sistema completo de pesquisa, preenchimento automático e histórico:
- Pesquisa com estabilização de imagem (300 ms)
- Exibir a lista de resultados da pesquisa (v-for)
- Salvar as últimas 5 pesquisas
- Clique em um item do histórico para pesquisar novamente
- Cancelar solicitações pendentes ao alterar os termos de pesquisa
- Implementar usando
watchEffecteonCleanup