Skip to content

<script setup>

<script setup> це синтаксичний цукор під час компіляції для використання композиційного АРІ всередині одно-файлових компонентів (SFC). Це рекомендований синтаксис, якщо ви використовуєте й одно-файлові компоненти, і композиційний АРІ. Він надає низку переваг перед звичайним синтаксисом <script>:

  • Більш лаконічний код з меншою кількістю boilerplate-коду
  • Можливість оголошувати реквізити та випромінювачі подій за допомогою чистого TypeScript
  • Краща продуктивність під час виконання (шаблон компілюється у функцію рендерингу в тій же області видимості, без проміжного проксі)
  • Краща продуктивність IDE для визначення типів (менше роботи для мовного сервера з вилучення типів з коду)

Базовий синтаксис

Щоб використовувати синтаксис, додайте атрибут setup до блоку <script>:

vue
<script setup>
console.log('привіт налаштування сценарію')
</script>

Код усередині компілюється як вміст функції setup() компонента. Це означає, що на відміну від звичайного <script>, який виконується лише один раз під час першого імпорту компонента, код всередині <script setup> виконуватиметься кожного разу, коли створюється екземпляр компонента.

Прив'язки верхнього рівня будуть доступні у шаблоні

Якщо використовується <script setup>, будь-які прив'язки верхнього рівня (включаючи змінні, оголошення функцій та імпорт), оголошені всередині <script setup>, можна використовувати безпосередньо в шаблоні:

vue
<script setup>
// змінна
const msg = 'Привіт!'

// функції
function log() {
  console.log(msg)
}
</script>

<template>
  <button @click="log">{{ msg }}</button>
</template>

Імпорти оголошуються так само. Це означає, що можна використовувати імпортовану допоміжну функцію у виразах шаблону, без необхідності оголошувати її через параметр methods:

vue
<script setup>
import { capitalize } from './helpers'
</script>

<template>
  <div>{{ capitalize('привіт') }}</div>
</template>

Реактивність

Реактивний стан потрібно створювати за допомогою Реактивного API. Аналогічно значенням, що повертаються з функції setup(), референції автоматично розгортаються, коли на них посилаються у шаблонах:

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

const count = ref(0)
</script>

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

Використання компонентів

Значення в області видимості <script setup> також можна використовувати безпосередньо як імена тегів власних компонентів:

vue
<script setup>
import MyComponent from './MyComponent.vue'
</script>

<template>
  <MyComponent />
</template>

Думайте про MyComponent як про змінну. Якщо ви використовували JSX, ментальна модель тут схожа. Еквівалент kebab-case <my-component> також працює в шаблоні, однак теги компонентів PascalCase настійно рекомендуються для узгодженості. Це також допомагає відрізнити їх від рідних користувацьких елементів.

Динамічні компоненти

Оскільки на компоненти посилаються як на змінні, а не реєструють їх під рядковими ключами, ми повинні використовувати динамічне прив'язування :is, коли використовуємо динамічні компоненти всередині <script setup>:

vue
<script setup>
import Foo from './Foo.vue'
import Bar from './Bar.vue'
</script>

<template>
  <component :is="Foo" />
  <component :is="someCondition ? Foo : Bar" />
</template>

Зверніть увагу, як компоненти можна використовувати як змінні в потрійному виразі.

Рекурсивні компоненти

Одно-файлові компоненти можуть неявно посилатися на себе за допомогою імені файлу. Наприклад, файл з ім'ям FooBar.vue може посилатися на себе як <FooBar/> у своєму шаблоні.

Зверніть увагу, що це має нижчий пріоритет, ніж імпортовані компоненти. Якщо є іменований імпорт, який конфліктує з іменем компонента від імені файлу, то можна задати псевдонім для імпортованого:

js
import { FooBar as FooBarChild } from './components'

Компоненти з простором назв

Ви можете використовувати теги компонентів із крапками, наприклад <Foo.Bar> для посилання на компоненти, вкладені у властивості об'єкта. Це корисно, коли ви імпортуєте кілька компонентів з одного файлу:

vue
<script setup>
import * as Form from './form-components'
</script>

<template>
  <Form.Input>
    <Form.Label>label</Form.Label>
  </Form.Input>
</template>

Використання директив користувача

Глобально зареєстровані користувацькі директиви працюють як зазвичай. Локальні спеціальні директиви не потрібно явно реєструвати за допомогою <script setup>, але вони повинні відповідати схемі імен vNameOfDirective:

vue
<script setup>
const vMyDirective = {
  beforeMount: (el) => {
    // зробити щось з елементом
  }
}
</script>
<template>
  <h1 v-my-directive>Це заголовок</h1>
</template>

Якщо ви імпортуєте директиву з іншого місця, її можна перейменувати відповідно до необхідної схеми іменування:

vue
<script setup>
import { myDirective as vMyDirective } from './MyDirective.js'
</script>

defineProps() & defineEmits()

Щоб оголосити такі параметри, як props і emits, із повною підтримкою виведення типу, ми можемо використовувати API defineProps і defineEmits, які автоматично доступні всередині <script setup>:

vue
<script setup>
const props = defineProps({
  foo: String
})

const emit = defineEmits(['change', 'delete'])
// код налаштування
</script>
  • defineProps і defineEmits є макросами компілятора, які можна використовувати лише всередині <script setup>. Їх не потрібно імпортувати, і вони компілюються під час обробки <script setup>.

  • defineProps приймає те саме значення, що й параметр props, а defineEmits приймає те саме значення, що й параметр emits.

  • defineProps і defineEmits забезпечують правильне визначення типу на основі переданих параметрів.

  • Параметри, передані в defineProps і defineEmits, будуть визначені з налаштування в область модуля. Тому параметри не можуть посилатися на локальні змінні, оголошені в області налаштування. Це призведе до помилки компіляції. Однак він може посилатися на імпортовані прив'язки, оскільки вони також є в області модуля.

Оголошення реквізитів/випромінення лише для типу

Реквізити та випромінення також можна оголосити за допомогою синтаксису чистого типу, передавши аргумент літерального типу в defineProps або defineEmits:

ts
const props = defineProps<{
  foo: string
  bar?: number
}>()

const emit = defineEmits<{
  (e: 'change', id: number): void
  (e: 'update', value: string): void
}>()

// 3.3+: альтернативний, більш стислий синтаксис
const emit = defineEmits<{
  change: [id: number] // синтаксис іменованого кортежу
  update: [value: string]
}>()
  • defineProps або defineEmits можуть використовувати лише декларацію під час виконання АБО декларацію типу. Використання обох одночасно призведе до помилки компіляції.

  • При використанні оголошення типу еквівалентне оголошення під час виконання автоматично генерується на основі статичного аналізу, щоб усунути потребу в подвійному оголошенні та все ще забезпечити правильну поведінку під час виконання.

  • У режимі розробника компілятор намагатиметься вивести відповідну перевірку з типів під час виконання. Наприклад, тут foo: String виведено з типу foo: string. Якщо тип є посиланням на імпортований тип, виведеним результатом буде foo: null (еквівалентно типу any), оскільки компілятор не має інформації про зовнішні файли.

  • У режимі продакшна компілятор згенерує оголошення формату масиву, щоб зменшити розмір пакета (реквізити тут буде скомпільовано в ['foo', 'bar'])

  • У версії 3.2 і нижче параметр загального типу для defineProps() обмежувався літералом типу або посиланням на локальний інтерфейс.

Це обмеження було вирішено в 3.3. Остання версія Vue підтримує посилання на імпортовані та обмежений набір складних типів у позиції параметра типу. Однак, оскільки перетворення типу під час виконання все ще базується на AST, деякі складні типи, які вимагають фактичного аналізу типу, наприклад умовні типи, не підтримуються. Ви можете використовувати умовні типи для типу окремого реквізиту, але не всього об'єкта реквізиту.

Значення реквізитів за замовчуванням під час використання оголошення типу

Одним із недоліків оголошення defineProps лише для типу є те, що воно не має способу надати значення за замовчуванням для реквізитів. Щоб вирішити цю проблему, також надається макрос компілятора withDefaults:

ts
export interface Props {
  msg?: string
  labels?: string[]
}

const props = withDefaults(defineProps<Props>(), {
  msg: 'привіт',
  labels: () => ['один', 'два']
})

Це буде скомпільовано до еквівалентних параметрів default. Крім того, помічник withDefaults забезпечує перевірку типу для значень за замовчуванням і гарантує, що повернутий тип props має видалені додаткові прапорці для властивостей, які мають оголошені значення за замовчуванням.

defineExpose()

Компоненти, які використовують <script setup>, за промовчанням закриті, тобто загальнодоступний екземпляр компонента, одержуваний через посилання у шаблоні або ланцюжок $parent, не оголошує доступу до будь-яких прив'язок усередині <script setup>.

Щоб явно оголосити властивості в компоненті з <script setup>, використовуйте макрос компілятора defineExpose:

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

const a = 1
const b = ref(2)

defineExpose({
  a,
  b
})
</script>

Коли батьківський компонент отримує екземпляр цього компонента через посилання шаблону, отриманий екземпляр матиме форму { a: number, b: number } (посилання автоматично розгортаються, як і у звичайних екземплярах).

defineOptions()

Цей макрос можна використовувати для оголошення параметрів компонента безпосередньо всередині <script setup> без використання окремого блоку <script>:

vue
<script setup>
defineOptions({
  inheritAttrs: false,
  customOptions: {
    /* ... */
  }
})
</script>
  • Підтримується лише в 3.3+.
  • Це макрос. Параметри буде піднято до області модуля, і вони не зможуть отримати доступ до локальних змінних у <script setup>, які не є літеральними константами.

defineSlots()

Цей макрос можна використовувати для надання підказок типу для IDE для перевірки імені слота та типу реквізитів.

defineSlots() приймає лише параметр типу, а не аргументи під час виконання. Параметр типу має бути літералом типу, де ключ властивості - це ім'я слота, а тип значення - функція слота. Перший аргумент функції - це атрибути, які слот очікує отримати, і його тип буде використано для атрибутів слота в шаблоні. Тип повернення наразі ігнорується та може бути any, але ми можемо використовувати його для перевірки вмісту слота в майбутньому.

Він також повертає об'єкт slots, який еквівалентний об'єкту slots, який відкривається в контексті налаштування або повертається useSlots().

vue
<script setup lang="ts">
const slots = defineSlots<{
  default(props: { msg: string }): any
}>()
</script>
  • Підтримується лише в 3.3+.

useSlots() & useAttrs()

Використання slots і attrs всередині <script setup> має бути відносно рідкісним, оскільки ви можете отримати до них прямий доступ як $slots і $attrs у шаблоні. У рідкісних випадках, коли вони вам потрібні, використовуйте помічники useSlots і useAttrs відповідно:

vue
<script setup>
import { useSlots, useAttrs } from 'vue'

const slots = useSlots()
const attrs = useAttrs()
</script>

useSlots і useAttrs - це фактичні функції середовища виконання, які повертають еквівалент setupContext.slots і setupContext.attrs. Їх також можна використовувати у звичайних функціях композиційного АРІ.

Використання разом із звичайною секцією <script>

<script setup> можна використовувати разом зі звичайним <script>. Звичайний <script> може знадобитися у випадках, коли нам потрібно:

  • Декларування параметрів, які не можуть бути виражені в <script setup>, наприклад inheritAttrs або спеціальні параметри, увімкнені через плагіни (Можна замінити на defineOptions у 3.3+).
  • Декларування іменного експорту.
  • Запуск побічних ефектів або створення об'єктів, які повинні виконуватися лише один раз.
vue
<script>
// звичайний <script>, виконується в межах модуля (лише один раз)
runSideEffectOnce()

// декларування додаткових параметрів
export default {
  inheritAttrs: false,
  customOptions: {}
}
</script>

<script setup>
// виконується в області setup() (для кожного екземпляра)
</script>

Підтримка поєднання <script setup> і <script> в одному компоненті обмежена сценаріями, описаними вище. зокрема:

  • НЕ використовуйте окремий розділ <script> для параметрів, які можна визначити за допомогою <script setup>, таких як props і emits.
  • Змінні, створені всередині <script setup>, не додаються як властивості до екземпляра компонента, що робить їх недоступними в опційному АРІ. Змішувати API таким чином настійно не рекомендується.

Якщо ви опинитеся в одному зі сценаріїв, який не підтримується, вам слід розглянути можливість переходу на явну функцію setup() замість використання <script setup> .

Верхньо-рівневий await

Верхньо-рівневий await можна використовувати всередині <script setup>. Отриманий код буде скомпільований як async setup():

vue
<script setup>
const post = await fetch(`/api/post/1`).then((r) => r.json())
</script>

Крім того, очікуваний вираз буде автоматично скомпільовано у форматі, який зберігає контекст поточного екземпляра компонента після await.

Примітка

async setup() потрібно використовувати в поєднанні з Suspen, який наразі все ще є експериментальною функцією. Ми плануємо доопрацювати та задокументувати його в майбутньому випуску, але якщо вам зараз цікаво, ви можете переглянути його тести, щоб побачити, як це працює.

Загальні типи

Параметри загального типу можна оголосити за допомогою атрибута generic тегу <script>:

vue
<script setup lang="ts" generic="T">
defineProps<{
  items: T[]
  selected: T
}>()
</script>

Значення generic працює точно так само, як список параметрів між <...> у TypeScript. Наприклад, ви можете використовувати кілька параметрів, обмеження extends, типи за замовчуванням і посилатися на імпортовані типи:

vue
<script
  setup
  lang="ts"
  generic="T extends string | number, U extends Item"
>
import type { Item } from './types'
defineProps<{
  id: T
  list: U[]
}>()
</script>

Restrictions

Через різницю в семантиці виконання модуля код всередині <script setup> покладається на контекст одно-файлових компонент. Переміщення у зовнішні файли .js або .ts може призвести до плутанини як для розробників, так і для інструментів. Таким чином, <script setup> не можна використовувати з атрибутом src.

<script setup> has loaded