[Medium] watch vs watchEffect
1. Что такое watch и watchEffect?
Что такое
watchиwatchEffect?
watch и watchEffect — это API Vue 3 для реакции на изменения реактивного состояния.
watch
Определение: явно наблюдает за одним или несколькими источниками и выполняет callback при их изменении.
<script setup>
import { ref, watch } from 'vue';
const count = ref(0);
const message = ref('Hello');
// наблюдение за одним источником
watch(count, (newValue, oldValue) => {
console.log(`count изменился с ${oldValue} на ${newValue}`);
});
// наблюдение за несколькими источниками
watch([count, message], ([newCount, newMessage], [oldCount, oldMessage]) => {
console.log('count или message изменились');
});
</script>
watchEffect
Определение: выполняется немедленно и автоматически отслеживает реактивные зависимости, используемые внутри callback.
<script setup>
import { ref, watchEffect } from 'vue';
const count = ref(0);
const message = ref('Hello');
// автоматически отслеживает count и message
watchEffect(() => {
console.log(`count: ${count.value}, message: ${message.value}`);
// перезапускается при изменении count/message
});
</script>
2. watch vs watchEffect: ключевые различия
Основные различия между
watchиwatchEffect
1. Объявление источника
watch: явные источники.
const count = ref(0);
const message = ref('Hello');
watch(count, (newVal, oldVal) => {
console.log('count изменился');
});
watch([count, message], ([newCount, newMessage]) => {
console.log('count или message изменились');
});
watchEffect: неявное отслеживание зависимостей.
const count = ref(0);
const message = ref('Hello');
watchEffect(() => {
console.log(count.value); // отслеживается автоматически
console.log(message.value); // отслеживается автоматически
});
2. Время выполнения
watch: ленивый по умолчанию; выполняется только после изменения источника.
const count = ref(0);
watch(count, (newVal) => {
console.log('выполнено');
});
count.value = 1; // запускает callback
watchEffect: выполняется немедленно, затем перезапускается при обновлении зависимостей.
const count = ref(0);
watchEffect(() => {
console.log('выполнено'); // немедленный первый запуск
console.log(count.value);
});
count.value = 1; // выполняется снова
3. Доступ к старому значению
watch: предоставляет newValue и oldValue.
const count = ref(0);
watch(count, (newVal, oldVal) => {
console.log(`с ${oldVal} на ${newVal}`);
});
watchEffect: прямого доступа к старому значению нет.
const count = ref(0);
watchEffect(() => {
console.log(count.value); // только текущее значение
});
4. Остановка наблюдателей
Оба возвращают функцию остановки.
const stopWatch = watch(count, (newVal) => {
console.log(newVal);
});
const stopEffect = watchEffect(() => {
console.log(count.value);
});
stopWatch();
stopEffect();
3. Когда использовать watch, а когда watchEffect?
Когда следует выбирать каждый API?
Используйте watch, когда
- Вам нужны явные источники.
watch(userId, (newId) => {
fetchUser(newId);
});
- Вам нужно старое значение.
watch(count, (newVal, oldVal) => {
console.log(`с ${oldVal} на ${newVal}`);
});
- Вам нужно ленивое выполнение.
watch(searchQuery, (newQuery) => {
if (newQuery.length > 2) {
search(newQuery);
}
});
- Вам нужен точный контроль (
immediate,deepи т.д.).
watch(
() => user.value.id,
(newId) => {
fetchUser(newId);
},
{ immediate: true, deep: true }
);
Используйте watchEffect, когда
- Вам нужно автоматическое отслеживание зависимостей.
watchEffect(() => {
if (user.value && permissions.value.includes('admin')) {
loadAdminData();
}
});
- Вам не нужно старое значение.
watchEffect(() => {
console.log(`текущий count: ${count.value}`);
});
- Вам нужен немедленный первый запуск.
watchEffect(() => {
updateChart(count.value, message.value);
});
4. Частые вопросы на собеседованиях
Частые вопросы на собеседованиях
Вопрос 1: порядок выполнения
Объясните вывод и порядок:
const count = ref(0);
const message = ref('Hello');
watch(count, (newVal) => {
console.log('watch:', newVal);
});
watchEffect(() => {
console.log('watchEffect:', count.value, message.value);
});
count.value = 1;
message.value = 'World';
Нажмите, чтобы увидеть ответ
watch ленивый (без немедленного запуска), но watchEffect выполняется немедленно.
Ожидаемая последовательность:
watchEffect: 0 Hello(начальный запуск)watch: 1(count изменился)watchEffect: 1 Hello(count изменился)watchEffect: 1 World(message изменился)
Ключевые моменты:
watchреагирует только на явно наблюдаемый источникwatchEffectреагирует на любую реактивную зависимость, используемую в callback
Вопрос 2: старое значение с watchEffect
Как получить доступ к старому значению при использовании watchEffect?
Нажмите, чтобы увидеть ответ
watchEffect не предоставляет старое значение напрямую.
Вариант 1: хранить собственный предыдущий ref
const count = ref(0);
const prevCount = ref(0);
watchEffect(() => {
console.log(`с ${prevCount.value} на ${count.value}`);
prevCount.value = count.value;
});
Вариант 2: использовать watch
watch(count, (newVal, oldVal) => {
console.log(`с ${oldVal} на ${newVal}`);
});
Рекомендация: если нужно старое значение, предпочитайте watch.
Вопрос 3: выбор watch или watchEffect
Выберите API для каждого сценария:
// Сценарий 1: перезагрузка данных пользователя при изменении userId
const userId = ref(1);
// Сценарий 2: активация кнопки отправки при валидности формы
const form = reactive({ username: '', password: '' });
const isValid = computed(() => form.username && form.password);
// Сценарий 3: поиск с debounce при изменении ключевого слова
const searchQuery = ref('');
Нажмите, чтобы увидеть ответ
Сценарий 1: изменение userId -> watch
watch(userId, (newId) => {
fetchUser(newId);
});
Сценарий 2: побочный эффект валидности формы -> watchEffect
watchEffect(() => {
submitButton.disabled = !isValid.value;
});
Сценарий 3: поиск с debounce -> watch
let timeoutId;
watch(searchQuery, (newQuery) => {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => {
search(newQuery);
}, 300);
});
Правило выбора:
- явный источник / старое значение / опции управления ->
watch - автоматическое отслеживание зависимостей + немедленный запуск ->
watchEffect
5. Лучшие практики
Лучшие практики
Рекомендуется
// 1) явный источник -> watch
watch(userId, (newId) => {
fetchUser(newId);
});
// 2) автоматическое отслеживание нескольких зависимостей -> watchEffect
watchEffect(() => {
if (user.value && permissions.value.includes('admin')) {
loadAdminData();
}
});
// 3) нужно старое значение -> watch
watch(count, (newVal, oldVal) => {
console.log(`с ${oldVal} на ${newVal}`);
});
// 4) очистка
onUnmounted(() => {
stopWatch();
stopEffect();
});
Избегайте
// 1) избегайте неуправляемых асинхронных побочных эффектов в watchEffect
watchEffect(async () => {
const data = await fetchData();
// потенциальная гонка/утечка, если не управлять
});
// 2) избегайте чрезмерного использования watchEffect
watchEffect(() => {
console.log(count.value); // watch(count, ...) может быть понятнее
});
// 3) избегайте мутации отслеживаемого источника в том же эффекте (риск циклов)
watchEffect(() => {
count.value++; // может вызвать бесконечный цикл
});
6. Итоги для собеседования
Итоги для собеседования
Быстрое запоминание
watch:
- явное объявление источника
- ленивый по умолчанию
- старое значение доступно
- лучше для контролируемых сценариев
watchEffect:
- автоматическое отслеживание зависимостей
- немедленное выполнение
- старого значения нет
- лучше для лаконичных реактивных побочных эффектов
Правило:
- явный контроль ->
watch - автоматическое отслеживание ->
watchEffect - нужно старое значение ->
watch - нужен немедленный начальный запуск ->
watchEffect
Пример ответа
В: В чём разница между watch и watchEffect?
Оба реагируют на реактивные изменения в Vue 3.
watchотслеживает явно объявленные источники и предоставляет старые/новые значения; по умолчанию ленивый.watchEffectвыполняется немедленно и автоматически отслеживает зависимости, используемые внутри callback, но не предоставляет старого значения. Используйтеwatchдля точности и контроля; используйтеwatchEffectдля автоматического сбора зависимостей.
В: Когда следует использовать каждый?
Используйте
watch, когда вам нужен явный контроль источника, старые значения или опции вроде debounce/deep/immediate. ИспользуйтеwatchEffect, когда вам нужен немедленный запуск и автоматическое отслеживание нескольких связанных реактивных значений.