Что такое watch и когда его использовать?

В Vue watch — это специальный способ отслеживания изменений определённых реактивных данных и выполнения пользовательской логики в ответ на эти изменения. Он особенно полезен, когда нужно реагировать на изменение значений переменных (например, data, props, computed), выполнять асинхронные действия, обращаться к API, производить побочные эффекты или синхронизировать состояние.

Основная идея

В отличие от computed, которые зависят от данных и возвращают значение, watch используется для наблюдения за изменениями и выполнения логики как побочного эффекта.

Объявление watch в Options API

В Options API (классический стиль Vue) watch объявляется внутри секции watch.

Пример:

<template>
<input v-model="searchQuery" />
</template>
<script>
export default {
data() {
return {
searchQuery: ''
}
},
watch: {
searchQuery(newVal, oldVal) {
console.log(\`searchQuery изменился с "${oldVal}" на "${newVal}"\`)
this.fetchResults(newVal)
}
},
methods: {
fetchResults(query) {
// Пример: запрос на сервер
console.log(\`Отправка запроса с параметром: ${query}\`)
}
}
}
</script>

Синтаксис параметров

watch: {
someData: {
handler(newVal, oldVal) {
// логика
},
immediate: true, // вызывается сразу при инициализации
deep: true // для глубокого отслеживания вложенных объектов
}
}

deep: true — глубокое отслеживание

По умолчанию watch не отслеживает вложенные изменения в объектах или массивах.

data() {
return {
user: {
name: 'Иван',
age: 30
}
}
},
watch: {
user: {
handler(newVal, oldVal) {
console.log('user изменился')
},
deep: true
}
}

Без deep: true, изменения, например this.user.name = 'Пётр', не вызовут срабатывание обработчика.

immediate: true — запуск при инициализации

Если вы хотите, чтобы watch сработал сразу после загрузки компонента (а не только после изменения значения), используйте immediate: true.

watch: {
settings: {
handler(newVal) {
this.applySettings(newVal)
},
immediate: true
}
}

Использование watch в Composition API

В Composition API используется функция watch, импортируемая из Vue:

import { ref, watch } from 'vue'
setup() {
const counter = ref(0)
watch(counter, (newVal, oldVal) => {
console.log(\`counter изменился с ${oldVal} на ${newVal}\`)
})
return { counter }
}

Отслеживание нескольких источников

Можно отслеживать несколько реактивных значений одновременно:

watch(\[refA, refB\], (\[newA, newB\], \[oldA, oldB\]) => {
console.log('Изменения:', newA, newB)
})

Примеры практического применения

Асинхронный API-запрос при изменении данных

watch(searchQuery, async (newVal) => {
const data = await fetch(\`/api/search?q=${newVal}\`).then(r => r.json())
results.value = data
})

Реакция на изменение маршрута (например, в Vue Router)

import { watch } from 'vue'
import { useRoute } from 'vue-router'
setup() {
const route = useRoute()
watch(() => route.query, (newQuery) => {
console.log('Параметры запроса изменились:', newQuery)
})
return {}
}

Сброс значения при изменении других данных

watch(selectedCategory, () => {
selectedProduct.value = null
})

Отличие от computed

Характеристика watch computed
Реагирует на Изменение реактивного значения Изменение зависимостей
--- --- ---
Поведение Запускает функцию (побочный эффект) Возвращает вычисленное значение
--- --- ---
Используется для API-запросов, логики, таймеров, логирования Отображения данных, преобразований
--- --- ---
Синхронность Может быть асинхронным Должен быть синхронным
--- --- ---

Пример неправильного использования watch

Не стоит использовать watch, если можно обойтись computed. Например:

watch(() => firstName.value + ' ' + lastName.value, val => {
fullName.value = val
})

Лучше заменить это на:

const fullName = computed(() => \`${firstName.value} ${lastName.value}\`)

Очистка ресурсов внутри watch

Когда вы используете watch, особенно с асинхронным кодом или подписками, можно использовать функцию onInvalidate, чтобы очистить предыдущие побочные эффекты:

watch(searchQuery, (val, \_, onInvalidate) => {
const controller = new AbortController()
fetch(\`/api?q=${val}\`, { signal: controller.signal })
onInvalidate(() => {
controller.abort()
})
})

Это предотвращает гонки и утечки памяти.

Автоматическая остановка при удалении компонента

watch в Composition API автоматически прекращает работу при размонтировании компонента, если он был создан внутри setup(). В Options API — то же самое, если watch был объявлен в watch: {}.

watch — мощный инструмент Vue, позволяющий реагировать на любые изменения реактивного состояния. Его главная сила — в контроле побочных эффектов, асинхронных действий, сложной логики, которая не подходит для computed или шаблонов.