Gernar
Frontend DeveloperVue.js

Интервью-вопрос

Что такое watch

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

Добавлен
Редакция

Подготовьте короткий ответ и пару деталей на случай уточняющих вопросов.

🐰0
🥚0

Мини-квиз

Проверка перед разбором

Несколько быстрых вопросов перед разбором. Так проще поймать места, которые только кажутся понятными.

Вопрос 1 из 50 правильно

Как лучше объяснить watch на интервью?

Вы хотите дать короткое определение без лишней теории.

Варианты ответа

Разбор

Разобраться, а не зазубрить

Дальше разбираем суть, типичные уточнения и места, где легко сказать лишнее или перепутать термины.

Базовая идея

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

Главное в ответе: watch нужен для побочного эффекта. Он не нужен, чтобы просто получить новую строку, число или объект для шаблона. Он нужен, когда изменение данных должно запустить действие: запрос, запись в хранилище, валидацию, аналитику или синхронизацию с внешним состоянием.

Как выбрать между watch и computed

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

Если вы делаете fullName из имени и фамилии, это computed. Если при смене userId нужно загрузить профиль, это watch. Такая граница показывает, что вы не используете watch как универсальный инструмент для любой реакции.

Как выбрать инструмент

1Нужно получить значение для шаблона из других данных?
Выбирайте computed.
2Нужно выполнить действие при изменении значения?
Выбирайте watch.
3Нужно отреагировать на ввод пользователя, но не хранить новое состояние?
Часто достаточно обработчика события или v-model.
4Нужно следить за несколькими фильтрами для одного запроса?
Используйте watch с массивом источников.

Пример безопасного watch для запроса

Типичный практический пример: компонент получает userId, и при его изменении нужно загрузить пользователя. Рискованный вариант просто пишет ответ каждого запроса в состояние. Если пользователь быстро переключит id, медленный старый ответ может прийти позже и показать не того пользователя.

Более безопасный вариант в Composition API использует cleanup и отменяет прошлый запрос.

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

const userId = ref('1');
const user = ref(null);
const loading = ref(false);
const error = ref(null);

watch(
  userId,
  async (id, _oldId, onCleanup) => {
    const controller = new AbortController();
    onCleanup(() => controller.abort());

    loading.value = true;
    error.value = null;

    try {
      const response = await fetch(`/api/users/${id}`, {
        signal: controller.signal,
      });

      if (!response.ok) {
        throw new Error('Failed to load user');
      }

      user.value = await response.json();
    } catch (e) {
      if (e.name !== 'AbortError') {
        error.value = e;
      }
    } finally {
      if (!controller.signal.aborted) {
        loading.value = false;
      }
    }
  },
  { immediate: true },
);
</script>

Здесь immediate: true нужен для первой загрузки, а onCleanup защищает UI от устаревшего запроса. Отдельные loading и error не дают экрану зависнуть в непонятном состоянии. Если API нельзя отменить, можно хранить текущий id запроса и перед записью результата проверять, что он все еще актуален.

Безопаснее
  1. 1Следить за конкретным id или фильтром
  2. 2Ставить loading перед запросом
  3. 3Показывать error или empty state вместо молчаливой пустоты
  4. 4Отменять или игнорировать устаревший запрос
  5. 5Писать результат только если он еще актуален
Опасно
  1. 1Следить за большим объектом целиком
  2. 2Запускать запрос на каждое мелкое изменение
  3. 3Не делать cleanup
  4. 4Не различать загрузку, ошибку и пустой результат
  5. 5Разрешать старому ответу перезаписать новый UI

Несколько источников и точная зависимость

Если эффект зависит от нескольких значений, в Composition API можно наблюдать массив источников. Это лучше, чем делать несколько почти одинаковых watcher.

watch([categoryId, page, sort], ([newCategoryId, newPage, newSort]) => {
  loadProducts({
    categoryId: newCategoryId,
    page: newPage,
    sort: newSort,
  });
});

Практический вывод: перечисляйте ровно те зависимости, которые запускают эффект. Watcher на большое состояние целиком чаще приводит к лишним запросам, сложной отладке и неожиданным срабатываниям.

Deep watching без магии

deep: true нужен, когда вы наблюдаете объект целиком и хотите реагировать на изменения внутри него. Но это не настройка на всякий случай. Чем больше объект, тем выше риск лишних срабатываний и затрат на отслеживание.

Часто лучше следить за конкретным полем или геттером:

watch(
  () => user.value.profile.email,
  (email) => {
    validateEmail(email);
  },
);

Так код честно показывает, от чего зависит эффект. Это проще читать, проще тестировать и сложнее случайно сломать при расширении объекта.

Как сформулировать ответ на интервью

Хороший короткий ответ может звучать так:

watch во Vue отслеживает изменение реактивного источника и запускает обработчик. Я использую его для побочных эффектов, например для запроса при смене id, валидации или синхронизации с URL. Если нужно просто получить производное значение для шаблона, я выберу computed. Для async-watch важно не забыть про отмену или игнорирование устаревших запросов.

Эта формулировка закрывает определение, отличие от computed и практический риск. Если у вас есть опыт с Vue 2 Options API, можно добавить, что идея та же, но синтаксис отличается: в Options API watcher описывают в секции watch, а в Composition API используют функцию watch.

Частые ошибки

Где обычно ошибаются

Проверьте формулировки, которые звучат уверенно, но на интервью быстро выдают пробелы.

  1. 1

    Заменять computed на watch

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

    Ставить deep: true без причины

    Глубокий watcher реагирует на вложенные изменения и может стать дорогим на больших объектах. Еще он часто скрывает плохую модель данных, когда компонент следит за всем объектом вместо нужного поля. Лучше наблюдать конкретный источник, например () => user.value.name.
  3. 3

    Не защищать async-watch от гонок

    Если пользователь быстро меняет фильтр, старый запрос может завершиться позже нового и перезаписать актуальные данные. В ответе стоит сказать про cleanup, AbortController или проверку актуального значения перед записью в состояние. Для строки поиска добавьте debounce, иначе UI будет дергаться, а клиент отправит лишние запросы.
  4. 4

    Следить за слишком широким источником

    Watcher на весь объект будет срабатывать чаще, чем нужно, и может запускать лишние запросы или валидацию. Это ухудшает производительность и усложняет отладку. Лучше выбрать минимальную зависимость, от которой реально зависит побочный эффект.
  5. 5

    Забывать про начальный запуск

    По умолчанию watch сработает после изменения, а не сразу. Если данные нужно загрузить по текущему id при открытии компонента, добавьте immediate: true или вызовите загрузку отдельно. Иначе экран может остаться пустым до первого изменения.

Follow-up

Что могут спросить дальше

Короткие ответы на вопросы, которыми проверяют понимание watch, computed и побочных эффектов во Vue.

Живые ответы

Видео с похожим вопросом

Если найдем публичные интервью с таким вопросом, добавим их сюда. Их удобно смотреть после теории, чтобы свериться с живыми ответами.

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

Содержание