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

Применение script setup синтаксиса в Vue 3 для упрощения компонентов

Автор

Олег Марков

Введение

С релизом Vue 3 разработчики получили не просто новый движок, но и фундаментальные изменения в работе с компонентами. Одно из сильнейших нововведений — синтаксис script setup. Это способ организации кода, который существенно упрощает написание и читаемость компонентов. Благодаря этому синтаксису можно не только избавиться от лишней "обвязки", но и писать код максимально просто, прозрачно и эффективно. В этой статье я покажу, как script setup повышает качество работы с Vue 3, разберу его основные возможности, проиллюстрирую использование на практике, а также дам рекомендации по переходу со старых подходов.

Преимущества и предназначение синтаксиса script setup

Сначала давайте разберемся, зачем вообще понадобился новый синтаксис.

Почему потребовалось обновление синтаксиса

До появления script setup большинство компонентов во Vue описывались или в Options API (с известными блоками props, data, methods, computed и так далее), или через Composition API (export default {}, с использованием функций в блоке script). Итог - очень много "болтовни", большого количества кода, который повторяется от компонента к компоненту, и запутывается между JavaScript и шаблоном.

script setup, появившийся в Vue 3.2 и выше, позволяет радикально сокращать подобную "обвязку", повышая скорость работы и читабельность. Это компиляторный фичер, который работает на этапе сборки кода, делая её максимально легкой для разработчика.

Преимущества script setup

  • Значительно меньше лишнего кода и шаблонных конструкций.
  • Лучшая поддержка TypeScript за счет автотипизации.
  • Более явное связывание JavaScript и шаблона.
  • Прямой доступ к переменным, функциям и реактивным данным внутри шаблона.
  • Локальный scoping и отсутствие конфликтов with this.

Давайте сразу на практическом примере посмотрим, в чем отличие.

Пример на классическом Composition API

<script>
import { ref } from 'vue'

export default {
  setup() {
    // Создаем реактивную переменную
    const counter = ref(0)
    // Функция для инкремента
    function increase() {
      counter.value++
    }
    // Возвращаем переменные и функции для шаблона
    return { counter, increase }
  }
}
</script>

<template>
  <button @click="increase">Value {{ counter }}</button>
</template>

Тот же пример на script setup

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

// Сразу определяем реактивную переменную
const counter = ref(0)
// Функция для инкремента
function increase() {
  counter.value++
}
</script>

<template>
  <button @click="increase">Value {{ counter }}</button>
</template>

Как видите, стало на порядок короче. Нет ни export default, ни return. Всё работает проще.

Как работает script setup под капотом

Давайте разберемся, почему этот синтаксис делает работу проще.

Краткая суть

  • Всё, что вы объявляете в блоке <script setup>, становится видимым в шаблоне (template).
  • Не нужно вручную что-то возвращать из функции setup, как это было раньше.
  • Все импорты, переменные, функции, реактивные свойства — доступны в шаблоне автоматически.
  • Поддерживается прямое подключение props, emits, slots и provide/inject.

Этот подход облегчает перенос кода, улучшает масштабируемость и тестируемость компонентов.

Как это работает при компиляции

Компилятор Vue обрабатывает <script setup> и генерирует необходимый "classical" Vue-компонент, просто избавляя вас от необходимости писать объёмный шаблон с return и export default. Все элементы из блока script setup автоматически инкапсулируются и подготавливаются для шаблона.

Использование props и emits с script setup

Одна из сильных сторон Vue — возможность определять пропсы и события (emits). Синтаксис script setup здесь тоже упрощает код.

Как объявлять props

Смотрите, как можно объявить пропсы:

<script setup>
defineProps({
  title: String,
  count: {
    type: Number,
    required: true
  }
})
</script>

<template>
  <h1>{{ title }} ({{ count }})</h1>
</template>

Если вы используете TypeScript, объявить пропсы можно так:

<script setup lang="ts">
interface Props {
  title: string
  count: number
}

const props = defineProps<Props>()
</script>

Особенности работы с defineProps

  • Если вы не сохраняете результат в переменную, поля становятся доступны напрямую — это удобно.
  • Если требуется сложная логика работы с пропсами, присваивайте результат в переменную const props = defineProps().

Как объявлять emits

Объединяем переход к событийному API через defineEmits:

<script setup>
const emit = defineEmits(['submit', 'cancel'])

function onSubmit() {
  // ...какая-то логика
  emit('submit')
}
</script>

В случае TypeScript вы можете типизировать события:

<script setup lang="ts">
const emit = defineEmits<{
  (event: 'submit', value: number): void,
  (event: 'cancel'): void
}>()
</script>

Теперь события строго типизированы, ошибок будет меньше.

Работа с реактивностью и вычисляемыми значениями

В script setup поддерживаются все возможности реактивности Vue — ref, reactive, computed и watch.

Пример с ref и computed

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

// Обычная реактивная переменная
const firstName = ref('')
const lastName = ref('')

// Вычисляемое значение
const fullName = computed(() => `${firstName.value} ${lastName.value}`)
</script>

<template>
  <label>
    Имя: <input v-model="firstName" />
  </label>
  <label>
    Фамилия: <input v-model="lastName" />
  </label>
  <p>Полное имя: {{ fullName }}</p>
</template>

Здесь fullName автоматически обновится, когда firstName или lastName изменится.

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

Всё как в Composition API, только никаких return и export default:

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

const msg = ref('Hello')

watch(msg, (newVal, oldVal) => {
  // Этот блок запустится при изменении msg
  console.log(`Сообщение изменилось: с "${oldVal}" на "${newVal}"`)
})
</script>

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

Иногда компоненты обмениваются состоянием через provide/inject. Синтаксис в script setup становится даже проще:

<!-- Provider.vue -->
<script setup>
import { provide } from 'vue'

const theme = ref('light')
provide('theme', theme)
</script>

<template><!-- ... --></template>
<!-- Consumer.vue -->
<script setup>
import { inject } from 'vue'

const theme = inject('theme', ref('default'))
</script>

<template>
  <div>Тема: {{ theme }}</div>
</template>

Работа с слотами в script setup

Вам не нужно явно описывать slots — они будут автоматически доступны через специальный объект useSlots.

Пример:

<script setup>
const slots = useSlots()
</script>

<template>
  <div>
    <header v-if="slots.header">
      <slot name="header"></slot>
    </header>
    <main>
      <slot></slot>
    </main>
  </div>
</template>

Реализация реактивных методов и обработчиков событий

Вам доступны любые методы и слушатели, а писать их в script setup особенно удобно.

<script setup>
const count = ref(0)

function increment() {
  count.value++
}

function reset() {
  count.value = 0
}
</script>

<template>
  <button @click="increment">+1</button>
  <button @click="reset">Сброс</button>
  <span>Значение: {{ count }}</span>
</template>

Использование внешних composables

Script setup отлично интегрируется с composables — переиспользуемыми функциями для логики.

// useCounter.js
import { ref } from 'vue'
export function useCounter() {
  const count = ref(0)
  const inc = () => count.value++
  return { count, inc }
}
<script setup>
import { useCounter } from './useCounter'

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

<template>
  <button @click="inc">+1</button>
  <span>{{ count }}</span>
</template>

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

Script setup был разработан с прицелом на лаконичную работу с TypeScript. Вам доступны:

  • Автоматическая типизация props и emits.
  • Type-only import (import type { MyType } from './types')
  • Явное определение структур без дублирования кода.

Пример:

<script setup lang="ts">
import type { Todo } from './types'

defineProps<{
  todos: Todo[]
}>()
</script>

Интеграция с серверным рендерингом (SSR)

Script setup полностью совместим с Nuxt 3 и другими решениями SSR на Vue 3. Работа со стейтом, props, emits и реактивностью происходит так же, как на клиенте.

Особенности scoped CSS с script setup

Никаких ограничений — используйте <style scoped> как обычно. script setup никак не мешает организации стилей и хорошо работает с CSS Variables, preprocessors, и модульными стилями.

Чем не является script setup

  • Это не замена всему, что было до этого — Options API продолжает поддерживаться.
  • Не подходит для случаев, когда нужен доступ к компоненту как объекту, например, для плагинов.
  • Некоторые advanced-patterны вроде render-функций вам придется реализовать иначе.

Советы по переходу на script setup

  1. Поочередно переписывайте компоненты, начиная с простейших.
  2. Если внутри компонента много сложных computed/watched свойств, переход окупится быстрее.
  3. Не забывайте тестировать функции в script setup через unit-тесты.
  4. Используйте linter и автотесты для контроля за переходом.

Заключение

Script setup — это современный способ написания компонентов во Vue 3, который уменьшает объем шаблонного кода, увеличивает прозрачность связей между логикой и представлением и делает интеграцию с TypeScript и разработку на Vue проще и приятнее. Несмотря на простой синтаксис, функционал не теряется: доступны все механизмы реактивности, composables, props, emits, slots и многое другое. Если вы хотите писать компоненты быстро и понятно, этот синтаксис станет незаменимым инструментом для вашей команды.


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

Как динамически определять список props, если их структура заранее неизвестна?

Используйте интерфейсы (в TypeScript) или функцию defineProps(), чтобы определить тип как Record<string, any>:

<script setup lang="ts">
const props = defineProps<Record<string, unknown>>()
</script>

Это позволит принимать любой набор ключей на уровне props.


Как подключать provide/inject для сложных типов с TypeScript?

Определяйте тип при вызове inject:

const user = inject<User>('userKey')

Как использовать глобальные миксины или плагины внутри script setup?

Если миксин содержит методы, импортируйте их как функции или используйте provide/inject. Для глобальных плагинов (например, $axios), обращайтесь через getCurrentInstance:

import { getCurrentInstance } from 'vue'
const { proxy } = getCurrentInstance()
proxy.$axios.get(…)

Можно ли объединять несколько блоков script setup в одном компоненте?

Нет, только ОДИН допустим на компонент. Для логического разбиения используйте обычный вместе с или выносите логику в composables.


Что делать, если вашему плагину нужен доступ к экземпляру компонента?

Получите доступ с помощью getCurrentInstance внутри script setup:

import { getCurrentInstance } from 'vue'
const instance = getCurrentInstance()

Теперь instance.proxy даст вам this-компонента, чтобы вызывать методы или свойства плагинов.


Стрелочка влевоРабота со скроллингом и прокруткой в Vue приложенияхИспользование scoped стилей для изоляции CSS в компонентах VueСтрелочка вправо

Все гайды по 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Использование Vue Devtools для отладки и мониторинга приложенийСоздание и настройка проектов 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
Открыть базу знаний