← Назад к вопросам

Когда нужно использовать Watch во Vue?

1.8 Middle🔥 151 комментариев
#Vue.js

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI2 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Когда нужно использовать Watch во Vue?

Что такое Watch

Watch в Vue 3 - это функция для отслеживания изменений реактивных данных. Когда значение меняется, можно запустить побочные эффекты (side effects).

import { watch, ref } from 'vue';

const count = ref(0);

watch(count, (newValue, oldValue) => {
  console.log(`Count изменился с ${oldValue} на ${newValue}`);
});

Основные сценарии использования

1. Синхронизация с внешними сервисами

Когда: Нужно отправить данные на сервер при изменении значения

const searchQuery = ref('');

watch(searchQuery, async (newQuery) => {
  if (newQuery.length === 0) return;
  
  // Отправляем запрос на поиск
  const results = await fetch(`/api/search?q=${newQuery}`)
    .then(r => r.json());
  
  searchResults.value = results;
});

Практический пример:

const userId = ref(null);
const user = ref(null);

watch(userId, async (newUserId) => {
  if (!newUserId) {
    user.value = null;
    return;
  }
  
  try {
    const response = await fetch(`/api/users/${newUserId}`);
    user.value = await response.json();
  } catch (error) {
    console.error('Ошибка загрузки пользователя', error);
  }
});

2. Отслеживание вложенных объектов

Когда: Нужно наблюдать за изменениями в объекте или массиве

const user = reactive({
  name: 'John',
  email: 'john@example.com',
  settings: {
    theme: 'dark',
    notifications: true
  }
});

// Отслеживать конкретное свойство
watch(
  () => user.settings.theme,
  (newTheme) => {
    document.documentElement.setAttribute('data-theme', newTheme);
    localStorage.setItem('theme', newTheme);
  }
);

// Отслеживать весь объект с deep: true
watch(
  user,
  (newUser) => {
    console.log('Пользователь изменился', newUser);
  },
  { deep: true }
);

3. Валидация данных в реальном времени

Когда: Нужна валидация при изменении значения

const email = ref('');
const emailError = ref('');

watch(email, (newEmail) => {
  // Проверяем email
  const isValid = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(newEmail);
  
  if (newEmail && !isValid) {
    emailError.value = 'Некорректный email';
  } else {
    emailError.value = '';
  }
});

4. Отслеживание нескольких источников (watchEffect)

Когда: Нужно реагировать на изменения нескольких переменных

const firstName = ref('John');
const lastName = ref('Doe');
const fullName = ref('');

// Способ 1: Несколько watch
watch([firstName, lastName], ([newFirst, newLast]) => {
  fullName.value = `${newFirst} ${newLast}`;
});

// Способ 2: watchEffect (автоматически отслеживает зависимости)
watchEffect(() => {
  fullName.value = `${firstName.value} ${lastName.value}`;
});

// watchEffect удобнее, когда зависимостей много

5. Задержанные операции (debounce)

Когда: Нужна задержка перед выполнением операции

const searchInput = ref('');
const searchResults = ref([]);
let timeoutId: number;

watch(searchInput, (newInput) => {
  // Очищаем предыдущий таймер
  clearTimeout(timeoutId);
  
  // Ставим новый с задержкой
  timeoutId = setTimeout(async () => {
    if (newInput.trim()) {
      const results = await fetch(`/api/search?q=${newInput}`)
        .then(r => r.json());
      searchResults.value = results;
    }
  }, 300);
});

Или используй composable:

import { useDebounceFn } from '@vueuse/core';

const searchInput = ref('');
const handleSearch = useDebounceFn(async (query: string) => {
  const results = await fetch(`/api/search?q=${query}`)
    .then(r => r.json());
  searchResults.value = results;
}, 300);

watch(searchInput, handleSearch);

6. Очистка ресурсов (cleanup)

Когда: Нужно отменить операцию при изменении зависимости

const userId = ref(1);

watch(userId, async (newUserId, oldUserId, onCleanup) => {
  let isMounted = true;
  
  // Функция очистки - вызывается перед новым watch или при unmount
  onCleanup(() => {
    isMounted = false;
  });
  
  const response = await fetch(`/api/users/${newUserId}`);
  const data = await response.json();
  
  // Проверяем, что компонент еще в DOM
  if (isMounted) {
    user.value = data;
  }
});

7. Выполнение функции при инициализации

Когда: Нужна функция, которая выполняется сразу и при изменении

const userId = ref(1);

watch(
  userId,
  async (newUserId) => {
    const userData = await fetchUser(newUserId);
    user.value = userData;
  },
  { immediate: true } // Выполнить сразу
);

Watch vs Computed vs watchEffect

Таблица сравнения:

ИнструментДля чегоПобочные эффекты
computedВычисляемое значениеНет (чистая функция)
watchРеакция на измененияДа (API запросы, логирование)
watchEffectРеакция на зависимостиДа (автоматический tracking)

Примеры:

// Computed - чистое вычисление
const fullName = computed(() => {
  return `${firstName.value} ${lastName.value}`;
});

// Watch - побочные эффекты
watch(fullName, (newName) => {
  // Отправить на сервер
  updateUserProfile({ fullName: newName });
});

// watchEffect - автоматический tracking
watchEffect(() => {
  console.log(`Пользователь: ${firstName.value} ${lastName.value}`);
  // Зависимости отслеживаются автоматически
});

Когда НЕ использовать Watch

Плохо - используется вместо computed:

// Неправильно
const total = ref(0);

watch([price, quantity], ([p, q]) => {
  total.value = p * q;
});

// Правильно
const total = computed(() => price.value * quantity.value);

Плохо - сложная логика в watch:

// Неправильно
watch(data, async () => {
  // 200 строк кода...
});

// Правильно - извлечь в функцию
const handleDataChange = async () => { /* ... */ };
watch(data, handleDataChange);

Best Practices

Всегда очищай ресурсы:

watch(query, async (newQuery, oldQuery, onCleanup) => {
  const controller = new AbortController();
  
  onCleanup(() => {
    controller.abort(); // Отменить fetch
  });
  
  const response = await fetch(`/api/search?q=${newQuery}`, {
    signal: controller.signal
  });
});

Используй правильный scope:

// Composable для переиспользования
export function useUserData(userId: Ref<number>) {
  const user = ref(null);
  
  watch(userId, async (id) => {
    user.value = await fetchUser(id);
  }, { immediate: true });
  
  return { user };
}

// Используй в компоненте
const userId = ref(1);
const { user } = useUserData(userId);

Типичные ошибки

Ошибка 1: Забыл immediate для начальной загрузки

// Компонент создан, но данные не загружены
watch(userId, async (id) => {
  user.value = await fetchUser(id);
});

// Правильно
watch(userId, async (id) => {
  user.value = await fetchUser(id);
}, { immediate: true });

Ошибка 2: Утечка памяти (забыл unwatch)

// В setup функции
const stop = watch(query, () => { /* ... */ });

// При unmount нужно вызвать
onBeforeUnmount(() => {
  stop();
});
Когда нужно использовать Watch во Vue? | PrepBro