Composition API детальный обзор

Composition API — современный подход к построению компонентов во Vue.js, введённый в версии 3. Основная цель — сделать код более организованным, повторно используемым и удобным для масштабирования. В отличие от Options API, где логика компонента распределяется по опциям (data, methods, computed, watch), Composition API объединяет связанную логику в функции, называемые композиционными функциями.

Главные элементы Composition API:

  • setup() — центральная функция компонента, в которой определяется состояние, методы и реактивная логика.
  • ref и reactive — реактивные объекты для хранения состояния.
  • computed — вычисляемые значения на основе реактивного состояния.
  • watch и watchEffect — отслеживание изменений данных.
  • Композиционные функции — пользовательские функции для инкапсуляции логики.

Функция setup()

setup() вызывается перед созданием компонента. В неё можно помещать реактивное состояние, функции и вычисляемые свойства. Важные особенности:

  • Аргументы: props, context (attrs, slots, emit).
  • Возвращаемое значение: объект, чьи свойства становятся доступными в шаблоне компонента.

Пример:

import { ref, computed } from 'vue';

export default {
  setup(props, { emit }) {
    const count = ref(0);

    const increment = () => {
      count.value++;
      emit('update', count.value);
    };

    const doubled = computed(() => count.value * 2);

    return { count, increment, doubled };
  }
}

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

ref используется для примитивов (числа, строки, булевы значения):

import { ref } from 'vue';

const message = ref('Hello World');
console.log(message.value); // доступ к значению через .value

reactive — для объектов и массивов. Создаёт глубоко реактивный объект:

import { reactive } from 'vue';

const state = reactive({
  user: { name: 'John', age: 30 },
  items: [1, 2, 3]
});

state.user.age = 31; // реактивно обновляет все привязанные вычисления и шаблоны

Ключевой момент: ref для примитивов, reactive для структурированных данных.


Вычисляемые свойства: computed

computed создаёт отслеживаемое, кэшируемое значение на основе реактивных данных:

import { ref, computed } from 'vue';

const count = ref(5);
const doubled = computed(() => count.value * 2);

console.log(doubled.value); // 10

Важное отличие от функций: вычисляемое свойство обновляется только при изменении зависимостей.


Наблюдатели: watch и watchEffect

watch позволяет реагировать на изменения конкретных данных:

import { ref, watch } from 'vue';

const count = ref(0);

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

watchEffect автоматически отслеживает все реактивные зависимости внутри функции:

import { ref, watchEffect } from 'vue';

const count = ref(0);

watchEffect(() => {
  console.log(`count сейчас: ${count.value}`);
});

Разница: watch требует явно указать источник, watchEffect сам определяет зависимости.


Композиционные функции

Композиционные функции позволяют инкапсулировать логику и повторно использовать её в разных компонентах. Они возвращают реактивные объекты или функции.

Пример:

import { ref } from 'vue';

export function useCounter(initial = 0) {
  const count = ref(initial);

  const increment = () => count.value++;
  const decrement = () => count.value--;

  return { count, increment, decrement };
}

Использование:

import { useCounter } from './useCounter';

export default {
  setup() {
    const { count, increment } = useCounter(5);
    return { count, increment };
  }
}

Работа с пропсами и событиями

В setup() доступ к пропсам через props:

export default {
  props: ['title'],
  setup(props) {
    console.log(props.title);
  }
}

Для вызова событий используется emit:

setup(props, { emit }) {
  const notify = () => emit('custom-event', 'данные');
  return { notify };
}

Важно: пропсы readonly, их нельзя изменять напрямую.


Слоты и атрибуты

В setup() можно работать с slots и attrs через context:

setup(props, { slots, attrs }) {
  return () => (
    <div {...attrs}>
      {slots.default ? slots.default() : 'Нет слота'}
    </div>
  );
}

slots.default() — рендеринг содержимого переданного слота.


Интеграция с Vue Router и Pinia

Composition API идеально подходит для интеграции с внешними библиотеками. Пример использования ref с Pinia:

import { defineStore } from 'pinia';
import { ref } from 'vue';

export const useCounterStore = defineStore('counter', () => {
  const count = ref(0);
  const increment = () => count.value++;
  return { count, increment };
});

В компоненте:

import { useCounterStore } from './stores/counter';

setup() {
  const counter = useCounterStore();
  return { counter };
}

Практические рекомендации

  • Группировать связанный код: состояние, вычисления и методы вместе.
  • Использовать композиционные функции для повторного использования логики.
  • Предпочитать ref для примитивов, reactive для объектов.
  • Вычисляемые свойства кэшировать через computed, чтобы уменьшить лишние вычисления.
  • Для наблюдения за изменениями данных использовать watch или watchEffect в зависимости от потребностей.

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