Если вы работаете с Vue.js, то Composition API — это не просто новая синтаксическая возможность, а фундаментальный сдвиг в мышлении. Он предлагает более гибкий и мощный способ организации логики компонентов, особенно когда они становятся сложными. Давайте разберемся, как он работает, и рассмотрим практические примеры, которые помогут вам освоить этот инструмент.
Что такое Composition API и зачем он нужен?
Composition API был представлен в Vue 3 как альтернатива классическому Options API (data, methods, computed). Его основная цель — решить проблему "распыления логики". В Options API код, относящийся к одной функциональности (например, работа с пользователем), мог быть разбросан по разным опциям: данные в data, методы в methods, а вычисляемые свойства в computed. В больших компонентах это усложняло понимание и поддержку.
Composition API построен вокруг реактивных ссылок (ref) и реактивных объектов (reactive), а также функций-композиций (composables), которые позволяют инкапсулировать и повторно использовать логику.
Базовый пример: от Options к Composition
Рассмотрим простой счетчик. Вот как он выглядел бы в Options API:
// Options API
<template>
<button @click=\"increment\">{{ count }}</button>
</template>
<script>
export default {
data() {
return {
count: 0
}
},
methods: {
increment() {
this.count++
}
}
}
</script>
А теперь тот же компонент с использованием Composition API и <script setup> (упрощенный синтаксис):
// Composition API с <script setup>
<template>
<button @click=\"increment\">{{ count }}</button>
</template>
<script setup>
import { ref } from 'vue'
const count = ref(0)
function increment() {
count.value++ // Обратите внимание на .value
}
</script>
Ключевые моменты:
ref()создает реактивную ссылку. Для примитивов (числа, строки) используетсяref.- Чтобы получить или изменить значение, нужно обращаться к свойству
.valueвнутри логики. В шаблоне Vue делает это автоматически. <script setup>автоматически экспортирует все объявления верхнего уровня в шаблон.
Работа с реактивными объектами и вычисляемыми свойствами
Для объектов и массивов часто удобнее использовать reactive(). Рассмотрим пример формы пользователя с вычисляемым полным именем.
<script setup>
import { reactive, computed } from 'vue'
const user = reactive({
firstName: 'Иван',
lastName: 'Петров'
})
const fullName = computed(() => {
return `${user.firstName} ${user.lastName}`
})
function updateName(newFirstName) {
user.firstName = newFirstName
}
</script>
Функция computed создает реактивное вычисляемое свойство. Его значение кэшируется и пересчитывается только при изменении зависимых реактивных источников.
Мощь композаблов (Composables)
Истинная сила Composition API раскрывается в создании переиспользуемых функций логики — композаблов. Давайте создадим композабл для работы с состоянием мыши.
// 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.pageX
y.value = event.pageY
}
onMounted(() => window.addEventListener('mousemove', update))
onUnmounted(() => window.removeEventListener('mousemove', update))
return { x, y }
}
Теперь используем его в компоненте:
<script setup>
import { useMouse } from './useMouse.js'
const { x, y } = useMouse()
</script>
<template>
<p>Координаты мыши: {{ x }}, {{ y }}</p>
</template>
Логика инкапсулирована и может быть использована в любом компоненте! Таким же образом можно создавать композаблы для работы с API, локальным хранилищем, таймерами и т.д.
Жизненный цикл и watch
Хуки жизненного цикла импортируются как функции (onMounted, onUpdated, onUnmounted). Для отслеживания изменений используется watch и watchEffect.
<script setup>
import { ref, watch, onMounted } from 'vue'
const searchQuery = ref('')
const searchResults = ref([])
// watch следит за конкретным источником
watch(searchQuery, async (newQuery) => {
if (!newQuery) {
searchResults.value = []
return
}
// Имитация запроса к API
searchResults.value = await fetchResults(newQuery)
})
// watchEffect запускается сразу и отслеживает все используемые внутри реактивные зависимости
watchEffect(() => {
console.log(`Запрос изменился на: ${searchQuery.value}`)
})
onMounted(() => {
console.log('Компонент смонтирован')
})
</script>
Практический пример: Компонент поиска с debounce
Соберем все вместе в полезном примере — поле поиска с задержкой (debounce), чтобы не делать запрос на каждый ввод символа.
<template>
<input v-model=\"inputValue\" placeholder=\"Поиск...\" />
<ul>
<li v-for=\"item in results\" :key=\"item.id\">{{ item.name }}</li>
</ul>
</template>
<script setup>
import { ref, watch } from 'vue'
const inputValue = ref('')
const results = ref([])
let timeoutId = null
watch(inputValue, (newVal) => {
// Очищаем предыдущий таймер
clearTimeout(timeoutId)
// Устанавливаем новый таймер на 500 мс
timeoutId = setTimeout(async () => {
if (newVal.trim()) {
results.value = await performSearch(newVal)
} else {
results.value = []
}
}, 500)
})
async function performSearch(query) {
// Заглушка для реального API-запроса
const response = await fetch(`/api/search?q=${query}`)
return response.json()
}
</script>
FAQ: Часто задаваемые вопросы о Composition API
Нужно ли полностью переписывать старые проекты на Composition API?
Нет. Composition API является дополнительной, а не обязательной системой. Vue 3 полностью поддерживает Options API. Переход можно делать постепенно, например, в новых компонентах или при рефакторинге сложной логики.
Что лучше использовать: ref или reactive?
ref универсальна и подходит для любых типов данных, но требует обращения через .value. reactive работает только с объектами, но позволяет обращаться к свойствам напрямую. Часто выбор — вопрос стиля. Многие разработчики предпочитают использовать ref для consistency, а для сложных структур состояния — reactive.
Composition API сложнее для новичков?
Options API изначально может быть более интуитивным благодаря четкой структуре. Composition API требует большего понимания реактивности и JavaScript. Однако для средних и крупных проектов он быстро окупается за счет лучшей организации кода и переиспользуемости логики.
Можно ли миксовать Composition и Options API в одном компоненте?
Да, но только если вы не используете <script setup>. В обычном компоненте с setup() хуком можно использовать Composition API, а остальные опции (например, components или inheritAttrs) оставить в Options API. Однако в <script setup> миксование невозможно.