Начальный 10 мин чтения
Composition API
Composition API — современный способ организации логики во Vue 3. Позволяет группировать связанный код и переиспользовать логику через composables.
Vue 3Composition APIsetupscript setup
Зачем нужен Composition API?
Options API (Vue 2) распределяет код по опциям: data, methods, computed, watch. При росте компонента связанная логика разбросана по всему файлу.
Composition API позволяет группировать логически связанный код вместе и выносить его в переиспользуемые функции.
<!-- Options API -->
<script>
export default {
data() {
return { count: 0, name: '' }
},
methods: {
increment() { this.count++ }
},
computed: {
doubled() { return this.count * 2 }
}
}
</script>
<!-- Composition API с <script setup> -->
<script setup>
import { ref, computed } from 'vue'
const count = ref(0)
const name = ref('')
const doubled = computed(() => count.value * 2)
function increment() {
count.value++
}
</script>
<script setup>
<script setup> — компактный синтаксис для Composition API. Всё объявленное в нём автоматически доступно в шаблоне.
<script setup lang="ts">
import { ref, computed, onMounted } from 'vue'
// Реактивные данные
const message = ref('Привет!')
const count = ref(0)
// Вычисляемое свойство
const upperMessage = computed(() => message.value.toUpperCase())
// Метод
function increment() {
count.value++
}
// Хук жизненного цикла
onMounted(() => {
console.log('Компонент смонтирован')
})
</script>
<template>
<h1>{{ upperMessage }}</h1>
<p>Счётчик: {{ count }}</p>
<button @click="increment">+</button>
</template>
ref и reactive
ref — для примитивов и объектов
<script setup>
import { ref } from 'vue'
const count = ref(0)
const name = ref('Иван')
const user = ref({ age: 25, role: 'admin' })
// Доступ через .value в скрипте
console.log(count.value) // 0
count.value = 5
// В шаблоне .value не нужен
// {{ count }} → 5
// {{ user.role }} → 'admin'
</script>
reactive — для объектов
<script setup>
import { reactive } from 'vue'
const state = reactive({
count: 0,
name: 'Иван',
items: [],
})
// Доступ без .value
state.count++
state.items.push('Новый элемент')
</script>
Когда что использовать:
ref— для примитивов, удобен при деструктуризацииreactive— для связанных данных, нет.value, но нельзя деструктурировать
computed
<script setup>
import { ref, computed } from 'vue'
const firstName = ref('Иван')
const lastName = ref('Иванов')
// Только чтение
const fullName = computed(() => `${firstName.value} ${lastName.value}`)
// Чтение и запись
const editableName = computed({
get: () => `${firstName.value} ${lastName.value}`,
set: (val) => {
const [first, ...rest] = val.split(' ')
firstName.value = first
lastName.value = rest.join(' ')
},
})
editableName.value = 'Пётр Петров'
// firstName.value → 'Пётр'
// lastName.value → 'Петров'
</script>
watch и watchEffect
<script setup>
import { ref, watch, watchEffect } from 'vue'
const query = ref('')
const results = ref([])
// watch — явное указание источника
watch(query, async (newVal, oldVal) => {
if (newVal.length > 2) {
results.value = await search(newVal)
}
}, { immediate: true, deep: false })
// watchEffect — автоматическое отслеживание зависимостей
watchEffect(async () => {
if (query.value.length > 2) {
results.value = await search(query.value)
}
})
</script>
Composables — переиспользование логики
Composable — функция, использующая Composition API. Заменяет миксины Vue 2.
// composables/useCounter.ts
import { ref, computed } from 'vue'
export function useCounter(initial = 0) {
const count = ref(initial)
const isNegative = computed(() => count.value < 0)
function increment() { count.value++ }
function decrement() { count.value-- }
function reset() { count.value = initial }
return { count, isNegative, increment, decrement, reset }
}
<!-- В компоненте -->
<script setup>
import { useCounter } from '@/composables/useCounter'
const { count, increment, decrement } = useCounter(10)
</script>
<template>
<button @click="decrement">-</button>
<span>{{ count }}</span>
<button @click="increment">+</button>
</template>
Props и Emits
<script setup lang="ts">
interface Props {
title: string
count?: number
}
const props = withDefaults(defineProps<Props>(), {
count: 0,
})
const emit = defineEmits<{
update: [value: number]
close: []
}>()
function handleClick() {
emit('update', props.count + 1)
}
</script>