Наблюдатели: watch и watchEffect

Разбираем наблюдатели watch и watchEffect — инструмент для побочных эффектов в ответ на изменение данных, и решаем, чем они отличаются от computed.

watch следит за конкретным источником и вызывает функцию при его изменении, давая старое и новое значение. watchEffect запускается сразу и сам отслеживает все реактивные значения, которые использует.

computed или watch?

Важно не путать их. computed вычисляет значение (например, итог корзины). watch не возвращает значение — он выполняет действие (побочный эффект): запрос к серверу, запись в localStorage, лог. Правило: нужно новое значение — computed; нужно что-то сделать в ответ на изменение — watch.

watch: следим за источником

<script setup>
import { ref, watch } from 'vue'

const query = ref('')

watch(query, (newVal, oldVal) => {
  console.log(`Запрос изменился: «${oldVal}» → «${newVal}»`)
  // здесь обычно делают fetch к серверу
})
</script>

Первый аргумент — за чем следим (ref, или функция-геттер для свойства), второй — что делать. Колбэк получает новое и старое значения, что удобно для сравнения.

Смоделируем watch на JS

Идею «реагируем на смену значения» можно показать обычным кодом — функция получает старое и новое и реагирует только на реальную смену:

function watch(getValue, callback) {
  let oldValue = getValue();
  return () => {
    const newValue = getValue();
    if (newValue !== oldValue) {
      callback(newValue, oldValue);
      oldValue = newValue;
    }
  };
}

let status = "offline";
const check = watch(() => status, (n, o) =>
  console.log(`Статус: ${o} → ${n}`)
);

check();              // значение не менялось — тишина
status = "online";
check();              // изменилось — сработает
status = "online";
check();              // то же значение — снова тишина
status = "away";
check();

Вывод:

Статус: offline → online
Статус: online → away

watchEffect: следит сам

watchEffect не требует указывать источник: он запускается сразу и автоматически отслеживает все реактивные значения, прочитанные внутри. Меняется любое из них — функция выполняется заново:

<script setup>
import { ref, watchEffect } from 'vue'

const count = ref(0)
const step = ref(1)

watchEffect(() => {
  // зависит и от count, и от step — Vue определит это сам
  console.log(`count=${count.value}, step=${step.value}`)
})
</script>

watch против watchEffect

watchwatchEffect
Источникуказываешь явноопределяется сам по чтению
Старое значениедоступнонедоступно
Первый запускпо умолчанию нетсразу при создании
Когда удобнеереакция на конкретный источникэффект от нескольких зависимостей

Типичные применения

  • Запрос к API при смене строки поиска или id (с задержкой/debounce).
  • Сохранение состояния в localStorage при каждом изменении.
  • Синхронизация с внешней библиотекой (карта, график).

Итог

  • watch следит за указанным источником и даёт старое/новое значение.
  • watchEffect запускается сразу и сам отслеживает использованные внутри реактивные данные.
  • Наблюдатели — для побочных эффектов, а computed — для производных значений.
  • Частые задачи: запросы к API, запись в localStorage, синхронизация с внешним кодом.
Проверьте себя
1. Когда использовать watch вместо computed?
AКогда нужно вычислить новое производное значение
BКогда нужно выполнить побочный эффект (запрос, запись в localStorage) в ответ на изменение данных
CКогда нужно отрисовать список
Dwatch и computed взаимозаменяемы
2. Чем watchEffect отличается от watch?
AwatchEffect нельзя использовать в script setup
BwatchEffect запускается сразу и сам определяет зависимости по прочитанным внутри значениям, а watch требует явного источника
CwatchEffect даёт старое значение, а watch — нет
DЭто полные синонимы
3. Какая задача типична для watch?
AСложить два числа для вывода в шаблоне
BОтправить запрос к серверу при изменении строки поиска
CДобавить CSS-класс по условию
DПеребрать массив в шаблоне
Поддержать проект