lenec ru

← все посты

Vue 3 Composition API vs Options API: миграция и паттерны

10K

Composition API — главное архитектурное изменение Vue 3. Он не заменяет Options API полностью, но решает проблемы, с которыми Options API не справлялся: переиспользование логики, типизация и организация кода в больших компонентах. Разберём оба подхода, покажем паттерны миграции и определим, когда какой API уместен.

Зачем Composition API: проблема mixins и логического разделения

В Options API логика разбита по опциям: data, methods, computed, watch. Когда компонент растёт, связанная логика разбросана по файлу. Фича «поиск» — переменная в data, метод в methods, вотчер в watch. Всё в разных местах.

Mixins решали переиспользование, но создавали новые проблемы:

  • Конфликты имён — два миксина с одинаковым свойством молча перезаписывают друг друга
  • Неявные зависимости — непонятно, откуда пришло this.loading
  • Плохая типизация — TypeScript не выводит типы из миксинов

Composition API решает все три: логика группируется по фичам, зависимости явные, TypeScript выводит типы автоматически.

setup() vs <script setup>

<!-- setup() внутри defineComponent -->
<script lang="ts">
import { ref, computed, defineComponent } from 'vue';

export default defineComponent({
  setup() {
    const count = ref(0);
    const doubled = computed(() => count.value * 2);
    const increment = () => { count.value++ };
    return { count, doubled, increment };
  },
});
</script>
<!-- <script setup> — рекомендуемый способ -->
<script setup lang="ts">
import { ref, computed } from 'vue';

const count = ref(0);
const doubled = computed(() => count.value * 2);
const increment = () => { count.value++ };
</script>

<template>
  <button @click="increment">{{ count }} × 2 = {{ doubled }}</button>
</template>

<script setup> — синтаксический сахар. Всё на верхнем уровне автоматически доступно в шаблоне. Не нужен return, не нужен defineComponent. Меньше boilerplate, лучше tree-shaking.

Реактивность: ref vs reactive, computed, watch

<script setup lang="ts">
import { ref, reactive, computed, watch, watchEffect } from 'vue';

// ref — для примитивов и любых значений
const name = ref<string>('');
const count = ref(0);

// reactive — для объектов (без .value, но нельзя переприсвоить)
const form = reactive({
  email: '',
  password: '',
  remember: false,
});

// computed — вычисляемое с кешированием
const isValid = computed(() =>
  form.email.includes('@') && form.password.length >= 8
);

// watch — реакция на изменения
watch(() => form.email, (newVal, oldVal) => {
  console.log(`Email: ${oldVal} → ${newVal}`);
});

// watchEffect — автотрекинг зависимостей
watchEffect(() => {
  console.log(`Count: ${count.value}`);
});
</script>

Правило: используй ref по умолчанию. reactive — только для объектов-форм, где удобнее без .value. Не смешивай подходы в одном компоненте.

Composables: переиспользуемая логика

Composable — функция, инкапсулирующая реактивную логику. Замена mixins без их проблем:

<!-- composables/useAuth.ts -->
<script lang="ts">
import { ref, computed } from 'vue';

interface User { id: string; name: string; role: string }
const user = ref<User | null>(null);

export function useAuth() {
  const isAuthenticated = computed(() => user.value !== null);

  async function login(email: string, password: string) {
    const res = await fetch('/api/login', {
      method: 'POST',
      body: JSON.stringify({ email, password }),
    });
    user.value = await res.json();
  }

  function logout() { user.value = null; }

  return { user, isAuthenticated, login, logout };
}
</script>
<!-- Использование -->
<script setup lang="ts">
import { useAuth } from '@/composables/useAuth';
const { user, isAuthenticated, logout } = useAuth();
</script>

<template>
  <div v-if="isAuthenticated">
    <span>{{ user?.name }}</span>
    <button @click="logout">Выйти</button>
  </div>
</template>

Преимущества: явный импорт, нет конфликтов имён, полная типизация, легко тестировать изолированно.

Миграция с Options API: пошаговый подход

Vue 3 поддерживает оба API одновременно — мигрируйте постепенно:

  1. Новые компоненты — пишите на <script setup>. Старые не трогайте.
  2. Извлекайте mixins в composables — один mixin = один composable.
  3. Мигрируйте по одному — начинайте с листовых компонентов. dataref, computedcomputed(), methods → функции, watchwatch().
  4. LifecyclemountedonMounted(), beforeUnmountonBeforeUnmount().
  5. Удаляйте this — в Composition API нет this. Все зависимости через замыкания.

Когда Options API всё ещё ок

  • Простые компоненты — кнопка, карточка, модалка с 2-3 пропсами и минимальной логикой
  • Команда новичков — Options API проще для входа: структура фиксирована
  • Легаси без планов на рефакторинг — если код работает и не растёт, миграция не оправдана
  • Прототипы — когда скорость важнее архитектуры

Для любого нового проекта на Vue 3 рекомендация однозначная: <script setup> + Composition API. Это стандарт экосистемы, лучше типизируется и масштабируется на большие кодовые базы. Options API никуда не денется — но это legacy-режим, а не путь вперёд.

Комментарии 0

  • Будьте первым, кто оставит комментарий.

Войдите, чтобы оставить комментарий.