Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Когда нужно использовать 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();
});