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


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:

JS
// ❌ 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 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

JS
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:


3. Sintaxe básica do watch()

(1) 4 parâmetros

JS
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

JS
const count = ref(0)

// Monitoring ref
watch(count, (newVal, oldVal) => {
  console.log(`count: ${oldVal} → ${newVal}`)
})

(3) Monitoramento de várias referências

JS
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

JS
const count = ref(0)
watch(count, (newVal, oldVal) => {
  console.log(`count: ${oldVal} → ${newVal}`)
})

(2) Escutando objetos reativos

JS
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

JS
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

JS
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

JS
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

JS
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

JS
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

JS
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

JS
// 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

VUE
<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>
▶ Experimente

▶ Exemplo: 2. Supressão de rebote na pesquisa

VUE
<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>
▶ Experimente

▶ Exemplo: 3. Observando mudanças na profundidade reativa

JS
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
▶ Experimente

▶ Exemplo: 4. Rastreamento automático com watchEffect

JS
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
▶ Experimente

▶ Exemplo: 5. Implementação de debounce e throttling

JS
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)
  }
})
▶ Experimente

▶ 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 watch ou watchEffect? R: Use watch sempre 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, embora watchEffect seja 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 o ref envolver um objeto, tanto o valor antigo quanto o novo são ref.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, use reactive ou uma função getter.

P: Ao usar watch para observar um objeto wrapper ref, 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 watch pode monitorar propriedades? R: Sim. watch(() => props.count, fn). Observe que as propriedades são somente para leitura; não as modifique na callback watch.

P: O que é oldVal quando immediate: 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 watch pode ser assíncrono? R: A função de retorno de chamada pode ser async, mas o watch em 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


📝 Exercícios

  1. Questões básicas (Dificuldade: ⭐)

    Use watch para monitorar uma variável ref count:

    • Quando a contagem mudar, exiba “contagem: ${antiga} → ${nova}”
    • Use immediate: true para executar a função de retorno de chamada imediatamente uma vez
  2. 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)
  3. Problema desafiador (Dificuldade: ⭐⭐⭐)

    Implementar um sistema completo de pesquisa, preenchimento automático e histórico:

    1. Pesquisa com estabilização de imagem (300 ms)
    2. Exibir a lista de resultados da pesquisa (v-for)
    3. Salvar as últimas 5 pesquisas
    4. Clique em um item do histórico para pesquisar novamente
    5. Cancelar solicitações pendentes ao alterar os termos de pesquisa
    6. Implementar usando watchEffect e onCleanup
Web-Tutorial.com

Equipe Técnica Web-Tutorial

Uma plataforma de tutoriais mantida por diversos desenvolvedores. Cada tutorial é escrito e revisado por profissionais da área correspondente. Trabalhamos para manter nosso conteúdo preciso e confiável — se encontrar algum problema, avise-nos.

100%