Что такое <script setup> и каковы его преимущества перед стандартным Composition API setup?
<script setup> — синтаксический сахар над setup(), где всё объявленное автоматически доступно в шаблоне без явного return. Он убирает шаблонный код, улучшает вывод типов и чуть ускоряет компиляцию.
Что такое `<script setup>`
<script setup> — это специальный атрибут блока <script> в однофайловых компонентах Vue 3 (SFC). Во время компиляции содержимое блока превращается в функцию setup(): все переменные, функции и импорты на верхнем уровне автоматически становятся доступны в шаблоне без явного return { ... }.
Сравнение с обычным Composition API
Обычный способ с явным возвратом:
<script>
import { ref, computed } from 'vue'
export default {
setup() {
const count = ref(0)
const double = computed(() => count.value * 2)
function increment() { count.value++ }
return { count, double, increment } // обязательно!
}
}
</script>
С <script setup>:
<script setup>
import { ref, computed } from 'vue'
const count = ref(0)
const double = computed(() => count.value * 2)
function increment() { count.value++ }
// return не нужен
</script>
<template>
<button @click="increment">{{ count }} (x2 = {{ double }})</button>
</template>
Преимущества
- Меньше шаблонного кода — не нужен
export default { setup() { return {} } }. - Лучший вывод типов TypeScript — компилятор видит переменные напрямую, IDE подсказывает типы в шаблоне.
- Производительность компилятора — Vite/Vue-компилятор может делать более точный tree-shaking.
- Автоматическая регистрация компонентов — импортированный компонент сразу доступен в шаблоне.
- Более чистый scoping — нет случайной утечки переменных через неверный
return.
defineProps и defineEmits
Макросы defineProps и defineEmits работают только внутри <script setup> и не требуют импорта:
<script setup lang="ts">
const props = defineProps<{
title: string
count?: number
}>()
const emit = defineEmits<{
(e: 'update', value: number): void
}>()
function send() {
emit('update', (props.count ?? 0) + 1)
}
</script>
defineExpose
В отличие от обычного setup(), компоненты с <script setup> по умолчанию закрыты для родителей. Чтобы открыть методы через ref, используйте defineExpose:
<script setup>
import { ref } from 'vue'
const inputRef = ref(null)
function focus() { inputRef.value?.focus() }
defineExpose({ focus })
</script>
Подводные камни
- Макросы (
defineProps,defineEmits,defineExpose,withDefaults) — только внутри<script setup>; вызов снаружи вызовет ошибку компилятора. - Нельзя смешивать
<script setup>и Options API в одном компоненте (но можно иметь два<script>-блока: один сsetup, один без — дляinheritAttrs: falseи т.п.). - Динамические компоненты через переменную (
:is="MyComp") работают только если переменная объявлена на верхнем уровне блока. awaitна верхнем уровне (top-level await) поддерживается, но оборачивает компонент вSuspense— убедитесь, что родитель его предоставляет.- Старые инструменты (Vetur, Jest без
@vue/vue3-jest) не понимают<script setup>— нужен свежий Volar и соответствующий трансформер. - Тип пропсов, объявленных через generic-синтаксис (
defineProps<{}>()), не может использовать импортированные типы из других модулей без флагаcompilerOptions.propsDestructureTransformв Vue < 3.3. - Reactive destructuring пропсов потеряет реактивность без
toRefsили Vue 3.3+definePropsdestructure.
Common mistakes
- Возвращать что-то вручную (
return { x }) — это лишний код, компилятор всё сделал. - Импортировать
definePropsявно. - Пытаться использовать
this. - Обращаться к локальным переменным извне без
defineExpose.
What the interviewer is testing
- Может показать минимальный
<script setup>-компонент. - Знает про автоматический импорт компонентов в шаблон.
- Понимает связь макросов и SFC-компилятора.
- Видит ограничение «только в SFC».