логотип PurpleSchool
логотип PurpleSchool

Понимание и применение Composition API в Vue 3

Автор

Олег Марков

Введение

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

Давайте разберёмся, зачем понадобился Composition API, какие у него ключевые возможности, а потом разберёмся в примерах — вы увидите, как он работает на практике и как его применять в повседневных задачах.

Для чего нужен Composition API

Кратко о мотивации появления

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

  • Трудно организовать и переиспользовать логику. Функции, связанные по смыслу, могут быть разбросаны между разными разделами компонента.
  • Переиспользование логики — только миксины. А миксины имеют массу недостатков: коллизии имён, запутанность зависимостей, неявные связи.

Composition API предлагает решение этих проблем.

Ключевые преимущества

  • Гибкость: вы сами решаете, как структурировать код.
  • Явные зависимости: видно, какие данные и функции используются и возвращаются из компонента.
  • Удобное переиспользование логики: с помощью composables — простых функций, которые вы можете вызывать повторно.

Давайте теперь перейдём к практике.

Основные концепции Composition API

setup функция — точка входа

Всё начинается с функции setup. Она вызывается сразу после создания экземпляра компонента, но до вычисления шаблона.

<script setup>
import { ref } from 'vue'

// Создаем реактивное состояние
const count = ref(0)

// Функция для увеличения значения
function increment() {
  count.value++
}
</script>

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

// Здесь создается переменная count как реактивное состояние, функция increment изменяет значение. Всё, что объявлено в setup, будет доступно внутри шаблона (в <script setup> это происходит автоматически).

Когда используется обычная форма setup

Если не использовать <script setup>, функция выглядит так:

export default {
  setup() {
    const count = ref(0)
    function increment() {
      count.value++
    }
    return { count, increment }
  }
}

// Объекты, возвращённые из setup, становятся доступны в шаблоне.

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

ref — для простых типов

ref создаёт реактивную ссылку на примитив или объект. Чтобы получить или изменить значение, используйте свойство .value.

import { ref } from 'vue'
const message = ref('Привет, Vue!')
message.value = 'Новый текст'

// Вы в любой момент можете присваивать новое значение через .value.

reactive — для объектов и массивов

Если работать хочется с объектами или массивами, удобней использовать reactive — он превращает весь объект целиком в реактивный:

import { reactive } from 'vue'
const state = reactive({
  counter: 0,
  user: {
    name: 'Аня'
  }
})

state.counter++           // Автоматически обновит шаблон
state.user.name = 'Иван'

// Здесь весь state становится реактивным, все его свойства отслеживаются Vue.

computed — вычисляемые значения

В Composition API вычисляемые значения создаются функцией computed. Это удобно для зависимых данных.

import { ref, computed } from 'vue'

const count = ref(2)
const double = computed(() => count.value * 2)

// double будет обновляться автоматически, если поменяется count.value.

watch и watchEffect — отслеживание изменений

Иногда нужно следить за изменениями переменных и запускать побочный эффект.

watch

watch позволяет следить за одной или несколькими реактивными переменными.

import { ref, watch } from 'vue'

const name = ref('Аня')
watch(name, (newValue, oldValue) => {
  // Будет вызван при каждом изменении name.value
  console.log(`Имя изменилось: ${oldValue} → ${newValue}`)
})

// Можно следить и за computed, и за ref, и за массивами.

watchEffect

watchEffect отслеживает все реактивные переменные, которые используются внутри его функции.

import { ref, watchEffect } from 'vue'

const price = ref(100)
watchEffect(() => {
  console.log(`Актуальная цена: ${price.value}`)
})

// Эффект будет запускаться каждый раз, когда price.value изменится.

Взаимодействие с пропсами и событиями

Получение пропсов

Внутри setup функция получает пропсы через первый аргумент:

export default {
  props: {
    userId: String
  },
  setup(props) {
    console.log(props.userId)
  }
}

// Props — это реактивный объект, можно наблюдать за их изменениями.

Вызов событий

Вторым аргументом в setup поступает функция emit:

export default {
  emits: ['save'],
  setup(props, { emit }) {
    function saveForm() {
      emit('save')
    }
    return { saveForm }
  }
}

// Теперь можете вызывать saveForm внутри шаблона, и событие "save" будет передано наружу.

Переиспользование логики: composables

Одна из главных "фишек" Composition API — возможность переиспользовать логику легко и прозрачно. Выносите повторяющийся код в отдельные функции.

// useCounter.js
import { ref } from 'vue'
export function useCounter() {
  const count = ref(0)
  function increment() {
    count.value++
  }
  return { count, increment }
}

И подключаете их в компонентах:

<script setup>
import { useCounter } from './useCounter'

const { count, increment } = useCounter()
</script>

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

// Здесь вы разбиваете код так, как удобно: бизнес-логику выносите в отдельные файлы и переиспользуете.

Ограничения и "подводные камни" Composition API

  • Отслеживание вложенных изменений: При использовании reactive с массивами и вложенными объектами не забывайте, что Vue отслеживает только существующие свойства. Если добавить новое свойство динамически, оно не станет реактивным.
  • ref внутри объектов: Если создать объект с полями типа ref, обращайтесь к ним всегда через .value или распаковывайте их через toRefs.
  • Типизация: При использовании TypeScript, Composition API работает гораздо удобнее и позволяет явно указывать типы ваших переменных.

Пример: Todo список на Composition API

Давайте соберём простой Todo-компонент с добавлением задач:

<script setup>
// Весь код внутри script setup будет доступен в шаблоне

import { ref } from 'vue'

const todos = ref([
  { text: 'Изучить Vue 3', done: false }
])
const newTodo = ref('')

function addTodo() {
  if (newTodo.value.trim()) {
    todos.value.push({
      text: newTodo.value,
      done: false
    })
    newTodo.value = ''
  }
}

function toggleTodo(index) {
  todos.value[index].done = !todos.value[index].done
}
</script>

<template>
  <div>
    <input v-model="newTodo" placeholder="Новая задача" />
    <button @click="addTodo">Добавить</button>
    <ul>
      <li v-for="(todo, index) in todos" :key="index">
        <label>
          <input type="checkbox" v-model="todo.done" @change="toggleTodo(index)" />
          <span :style="{ textDecoration: todo.done ? 'line-through' : 'none' }">{{ todo.text }}</span>
        </label>
      </li>
    </ul>
  </div>
</template>

// Здесь понятно видно — состояние лежит в ref, вся логика аккуратно размещена внутри setup, подключение в шаблоне выглядит просто.

Взаимодействие с жизненным циклом компонента

Для реакций на жизненный цикл компонента предусмотрены специальные хуки: onMounted, onUpdated, onUnmounted, onBeforeMount и другие.

import { ref, onMounted, onUnmounted } from 'vue'

const timer = ref(0)
let intervalId

onMounted(() => {
  intervalId = setInterval(() => {
    timer.value++
  }, 1000)
})

onUnmounted(() => {
  clearInterval(intervalId)
})

// onMounted работает аналогично mounted, но теперь его можно вызывать сколько угодно раз и в любом месте setup либо в composables.

Интеграция с Options API

Многие проекты переходят на Composition API постепенно. Композиционный и опционный API могут сосуществовать в одном проекте и даже в одном компоненте.

export default {
  data() {
    return { a: 10 }
  },
  setup() {
    const b = ref(20)
    return { b }
  }
}

// В шаблоне этого компонента будет доступно и a, и b.

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

Composition API предоставляет удобный способ делиться состоянием между вложенными компонентами через provide и inject.

// Родительский компонент
import { provide, ref } from 'vue'
export default {
  setup() {
    const color = ref('blue')
    provide('color', color)
  }
}

// Дочерний компонент
import { inject } from 'vue'
export default {
  setup() {
    const color = inject('color')
    // Теперь color можно использовать в шаблоне
    return { color }
  }
}

// Значения, переданные через provide, поддерживают реактивность, если делитесь ref или reactive.

Реализация сложных логик с помощью composables

Рассмотрим реальный пример: создание composable, который будет отслеживать положение мыши.

// useMouse.js
import { ref, onMounted, onUnmounted } from 'vue'

export function useMouse() {
  const x = ref(0)
  const y = ref(0)

  function update(event) {
    x.value = event.clientX
    y.value = event.clientY
  }

  onMounted(() => window.addEventListener('mousemove', update))
  onUnmounted(() => window.removeEventListener('mousemove', update))
  
  return { x, y }
}
// В компоненте
<script setup>
import { useMouse } from './useMouse'

const { x, y } = useMouse()
</script>

<template>
  <p>Координаты мыши: {{ x }}, {{ y }}</p>
</template>

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

Организация больших проектов с помощью Composition API

  • Группируйте бизнес-логику по отдельным composables. Один composable — одна задача (например, форма, запрос к API, работа с локальным состоянием).
  • Используйте отдельные папки для composables. Обычно их кладут в src/composables.
  • Используйте TypeScript для декларации возвращаемых типов и аргументов.

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


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

Частозадаваемые технические вопросы по теме

Как получить доступ к this внутри setup?

В функции setup не существует контекста this, вместо этого используйте переменные, созданные внутри функции, либо повторно используйте ссылки из composables. Для доступа к свойствам компонента используйте первый аргумент функции (props), события через { emit }, а роутинг или store — импортируйте напрямую.

Как вызвать методы жизненного цикла, если логика вынесена в composable?

Просто импортируйте хук (например, onMounted) из 'vue' и вызывайте его прямо внутри вашего composable. Vue корректно зарегистрирует все обработчики.

// useInit.js
import { onMounted } from 'vue'
export function useInit() {
  onMounted(() => {
    // логика запуска
  })
}

Можно ли использовать v-model с Composition API?

Да, вы можете использовать v-model, если возвращаете переменную из setup. Чтобы настроить v-model на кастомный компонент, используйте emit для события 'update:modelValue', и принимайте 'modelValue' как props.

Как работает provide/inject в Composition API, если значение поменялось?

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

Как типизировать props, возвращаемые значения и зависимости в Composition API при использовании TypeScript?

Декларируйте типы props через defineProps<[тип]>(), возвращаемые значения setup — через интерфейсы, composables — через явные типы. Используйте тип Ref для ref-переменных:

import { Ref } from 'vue'
const count: Ref<number> = ref(0)
Стрелочка влевоПонимание core функционала Vue и его применениеПонимание и работа с компилятором VueСтрелочка вправо

Постройте личный план изучения Vue до уровня Middle — бесплатно!

Vue — часть карты развития Frontend

  • step100+ шагов развития
  • lessons30 бесплатных лекций
  • lessons300 бонусных рублей на счет

Бесплатные лекции

Все гайды по Vue

Руководство по валидации форм во Vue.jsИнтеграция Tiptap для создания редакторов на VueРабота с таблицами во Vue через TanStackИнструкция по установке и компонентам Vue sliderУправление пакетами Vue js с помощью npmУправление пакетами и node modules в Vue проектахКак использовать meta для улучшения SEO на VueПолный гайд по компоненту messages во Vuejs5 правил использования Inertia с Vue и LaravelРабота с модулями и пакетами в VueИнструкция по работе с grid на VueGithub для Vue проектов - подробная инструкция по хранению и совместной работеНастройка ESLint для Vue проектов и поддержка качества кодаОбработка ошибок и отладка в Vue.jsИспользование Vue Devtools для отладки и мониторинга приложенийРабота с конфигурационными файлами и скриптами VueСоздание и настройка проектов Vue с помощью Vue CLI3 способа интеграции Chart.js с Vue для создания графиковРабота с Canvas во VueИнструкция по реализации календаря во VueРабота с Ant Design Vue для создания UI на Vue
Обзор и использование утилит Vue для удобной разработкиРабота с обновлениями компонента и жизненным циклом updateРазрешение конфликтов и ошибок с помощью Vue resolveИспользование query-параметров и их обработка в маршрутах VueЗагрузка и управление состоянием загрузки в VueИспользование библиотек Vue для расширения функционалаРабота с JSON данными в приложениях VueКак работать с экземплярами компонента Instance во VueПолучение данных и API-запросы во Vue.jsЭкспорт и импорт данных и компонентов в VueОбработка событий и их передача между компонентами VuejsГайд по defineEmits на Vue 3Понимание core функционала Vue и его применениеПонимание и применение Composition API в Vue 3Понимание и работа с компилятором VueКогда и как использовать $emit и call во VueВзаимодействие с внешними API через Axios в Vue
Веб приложения на Vue архитектура и лучшие практикиИспользование Vite для быстрого старта и сборки проектов на Vue 3Работа с URL и ссылками в приложениях на VueРабота с пользовательскими интерфейсами и UI библиотеками во VueОрганизация и структура исходных файлов в проектах VueИспользование Quasar Framework для разработки на Vue с готовыми UI-компонентамиОбзор популярных шаблонов и стартовых проектов на VueИнтеграция Vue с PHP для создания динамичных веб-приложенийКак организовать страницы и маршруты в проекте на VueNuxt JS и Vue 3 для SSR приложенийСоздание серверных приложений на Vue с помощью Nuxt jsИспользование Vue Native для разработки мобильных приложенийОрганизация и управление индексной страницей в проектах VueИспользование Docker для контейнеризации приложений на VueИнтеграция Vue.js с Django для создания полноценных веб-приложенийСоздание и работа с дистрибутивом build dist Vue приложенийРабота со стилями и CSS в Vue js для красивых интерфейсовСоздание и структурирование Vue.js приложенияКак исправить ошибку cannot find module vueНастройка и сборка проектов Vue с использованием современных инструментовИнтеграция Vue с Bitrix для корпоративных решенийРазработка административных панелей на Vue js
5 библиотек для создания tree view во VueИнтеграция Tailwind CSS с Vue для современных интерфейсовИнтеграция Vue с серверной частью и HTTPS настройкамиКак обрабатывать async операции с Promise во VueИнтеграция Node.js и Vue.js для разработки приложенийРуководство по интеграции Vue js в NET проектыПримеры использования JSX во VueГайд по импорту и регистрации компонентов на VueМногоязычные приложения на Vue с i18nИнтеграция FLIR данных с Vue5 примеров использования filter во Vue для упрощения разработки3 примера реализации drag-and-drop во Vue
Управление переменными и реактивными свойствами во VueИспользование v for и slot в VueПрименение v-bind для динамической привязки атрибутов в VueУправление пользователями и их данными в Vue приложенияхСоздание и использование UI Kit для Vue приложенийТипизация и использование TypeScript в VuejsИспользование шаблонов в Vue js для построения интерфейсовИспользование Swiper для создания слайдеров в VueРабота со стилями и стилизацией в VueСтруктура и особенности Single File Components SFC в VueРабота со SCSS в проектах на Vue для стилизацииРабота со скроллингом и прокруткой в Vue приложенияхПрименение script setup синтаксиса в Vue 3 для упрощения компонентовИспользование scoped стилей для изоляции CSS в компонентах Vue3 способа улучшить навигацию Vue с push()Обработка запросов и асинхронных операций в VueПонимание и использование provide inject для передачи данных между компонентамиПередача и использование props в Vue 3 для взаимодействия компонентовПередача данных между компонентами с помощью props в Vue jsУправление property и функциями во Vue.jsРабота со свойствами компонентов VueУправление параметрами и динамическими данными во VueРабота с lifecycle-хуком onMounted во VueОсновы работы с объектами в VueПонимание жизненного цикла компонента Vue js на примере mountedИспользование модальных окон modal в Vue приложенияхИспользование методов в компонентах Vue для обработки логикиИспользование метода map в Vue для обработки массивовИспользование хуков жизненного цикла Vue для управления состоянием компонентаРабота с ключами key в списках и компонентах VueОбработка пользовательского ввода в Vue.jsРабота с изображениями и их оптимизация в VueИспользование хуков жизненного цикла в VueОрганизация сеток и гридов для верстки интерфейсов на VueСоздание и управление формами в VueОрганизация файлов и структура проекта Vue.jsКомпоненты Vue создание передача данных события и emitРабота с динамическими компонентами и данными в Vue3 способа манипулирования DOM на VueРуководство по div во VueИспользование директив в Vue и их расширенные возможностиОсновы и применение директив в VueИспользование директив и их особенности на Vue с помощью defineИспользование компонентов datepicker в Vue для выбора датОрганизация циклов и итераций во VueКак работает компиляция Vue CoreСоздание и использование компонентов в Vue JSОбработка кликов и пользовательских событий в VueИспользование классов в Vue для организации кода и компонентовИспользование директивы checked для управления состоянием чекбоксов в VueГайд на checkbox компонент во VueОтображение данных в виде графиков с помощью Vue ChartСоздание и настройка кнопок в VueСоздание и настройка кнопок в Vue приложенияхРабота с lifecycle-хуками beforeCreate и beforeMount во VueИспользование массивов и методов их обработки в VueИспользование массивов и их обработка в Vue
Использование Vuetify для создания современных интерфейсов на VueИспользование transition во VueТестирование компонентов и приложений на VueРабота с teleport для управления DOM во VueПять шагов по настройке SSR в VuejsИспользование Shadcn UI компонентов с Vue для продвинутых интерфейсовИспользование router-link для навигации в Vue RouterКак использовать require в Vue для динамического импорта модулейРабота с динамическим рендерингом и виртуальным DOM на Vue.jsИспользование ref для управления ссылками и реактивностью в Vue 3Использование Vue Pro и его преимущества для профессиональной разработкиРуководство по nextTick для работы с DOMСоздание и использование компонентов с помощью Vue js и CУправление состоянием и реактивностью через inject и provideДинамическое обновление компонентов и данных на VueГлубокое изучение документации Vue и как эффективно её использоватьИспользование Crystal с Vue для разработкиИспользование вычисляемых свойств для динамического отображения данных на Vue jsОптимизация производительности и предупреждения в Vue
Открыть базу знаний

Лучшие курсы по теме

изображение курса

Vue 3 и Pinia

Антон Ларичев
AI-тренажеры
Практика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.9
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

TypeScript с нуля

Антон Ларичев
AI-тренажеры
Практика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.7
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

Next.js - с нуля

Антон Ларичев
AI-тренажеры
Практика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.7
3 999 ₽ 6 990 ₽
Подробнее

Отправить комментарий