Composition API поменял способ, которым разработчики думают о компонентах во Vue. Это не просто новый синтаксис, это возможность группировать связанную логику вне зависимости от жизненного цикла и шаблона. В этой статье я пошагово объясню ключевые примитивы, паттерны организации кода и практические приёмы, которые помогут перейти от теории к надёжным компонентам.

Зачем появился Composition API

Options API был понятен и удобен для небольших компонентов, но по мере роста приложения логика перемешивалась между хуками и опциями, что усложняло поддержку. Composition API предлагает другой способ: собирать функциональность по смыслу, а не по типам опций.

Он облегчает повторное использование кода и упрощает тестирование. Вместо разбросанных методов и computed-свойств вы получаете функции, которые можно инкапсулировать и экспортировать как отдельные блоки логики.

Основные примитивы и как ими пользоваться

В центре Composition API лежат несколько простых, но мощных инструментов: ref, reactive, computed и watch. Каждый из них решает конкретную задачу — от простого состояния до отслеживания изменений и вычисляемых значений.

Ниже таблица в двух колонках показывает соответствие задач и примитивов.

Задача Примитив
Скалярное реактивное значение ref()
Реактивный объект с вложенностью reactive()
Вычисляемое значение computed()
Наблюдение за изменениями watch(), watchEffect()

Код примера иллюстрирует базовый шаблон setup-функции и работу с ref и computed.

import { ref, computed } from 'vue';

export default {
  setup() {
    const count = ref(0);
    const doubled = computed(() => count.value * 2);
    function increment() {
      count.value++;
    }
    return { count, doubled, increment };
  }
}

Composables — как организовать повторяемую логику

Composables это просто функции, которые возвращают реактивные свойства и методы. Они помогают выносить повторяющийся код из компонентов и делают его независимым от шаблона.

Правила просты: одна функция — одна ответственность, минимальный набор побочных эффектов и хорошая читаемость API. Тогда composable легко тестировать и комбинировать.

  • Держите composable маленькими и предсказуемыми.
  • Не импортируйте компоненты внутри composable — пусть это будут только данные и логика.
  • Используйте именованные возвращаемые свойства для ясности.

Пример: useFetch для загрузки данных из API, который возвращает состояние загрузки, ошибку и функцию для повторной загрузки.

Реактивность: тонкости ref и reactive

ref подходит для примитивов и значений, у которых есть свойство value. reactive удобен для объектов и массивов — он возвращает прокси с реактивностью на уровне полей. Важно помнить о поведении при деструктуризации: деструктурируя reactive-объект, вы потеряете реактивность, а деструктурируя ref нужно использовать .value.

Если нужно передать реактивный объект в библиотеку, ожидающую обычный объект, часто удобно использовать toRaw или toRefs. toRefs помогает сохранить реактивность при распаковке полей объекта и делает bind к шаблону безопасным.

watch и watchEffect — когда что использовать

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

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

Интеграция с шаблоном и реактивный рендеринг

В шаблонах Vue видно, насколько природа ref удобна: обращение к value внутри setup экспортируется в компонент и в шаблоне используется без .value. Это снижает визуальный шум и делает шаблон чище.

При работе с reactive проще оперировать как с обычным объектом, но не забывайте о reactivity caveats при передаче данных между функциями и компонентами. Я часто сначала делаю объект reactive, а затем возвращаю его через toRefs для удобного связывания в шаблоне.

Типизация с TypeScript

Composition API гораздо удобнее типизируется, чем Options API, благодаря явным входным и выходным значениям функций. Вы можете объявить типы для входных параметров composable и для возвращаемого объекта, что даёт понятную подсказку в редакторе и снижает количество ошибок времени выполнения.

Типизация ref и reactive потребует внимания: рекомендуется явно указывать тип значения в ref для ясности. Для сложных состояний можно использовать интерфейсы и Partial, чтобы покрыть варианты начальной инициализации.

Переход с Options API: подходы и подводные камни

Переход не обязательно делать разом. Хорошая тактика — начать с новых компонентов и постепенно рефакторить крупные компоненты по частям, вынося связанный функционал в composables. Это позволяет сохранять рабочую кодовую базу и получать выигрыш от нового подхода шаг за шагом.

Главная ловушка — чрезмерная абстракция. Если вынести всё в composables без смысла, код станет менее читаемым. Лучше ориентироваться на повторяемость и ясные границы ответственности.

Практические советы и мой опыт

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

Ещё одна рекомендация — писать небольшие тесты на composables. Даже простые unit-тесты, проверяющие изменение состояний и реакции на события, экономят часы отладки в будущем. Тестируемые функции — это спокойствие при рефакторинге.

Пример реального composable

Ниже краткий пример useLocalStorage, который синхронизирует реактивное значение с localStorage. Он показывает, как легко инкапсулировать побочные эффекты и предоставить простой API компонентам.

import { ref, watch } from 'vue';

export function useLocalStorage(key, initial) {
  const stored = localStorage.getItem(key);
  const state = ref(stored ? JSON.parse(stored) : initial);

  watch(state, (val) => {
    localStorage.setItem(key, JSON.stringify(val));
  }, { deep: true });

  return state;
}

Компонент может импортировать эту функцию и работать с state как с обычным реактивным значением. Такой подход уменьшает дублирование и делает поведение предсказуемым.

Куда двигаться дальше

После освоения базовых примитивов и нескольких composables имеет смысл изучить библиотечные решения, которые строятся на Composition API — например, Vue Router и Pinia предоставляют интеграцию на уровне setup. Это расширяет инструментарий и помогает выстроить архитектуру приложения.

Важно экспериментировать в небольших срезах приложения, фиксировать подходы и оформлять шаблоны переиспользования. Чем яснее договорённости в команде, тем проще поддерживать кодовую базу, где Composition API выступает связующим элементом для чистой и модульной логики.