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, будуть визначені з налаштування в область модуля. Тому параметри не можуть посилатися на локальні змінні, оголошені в області налаштування. Це призведе до помилки компіляції. Однак він може посилатися на імпортовані прив'язки, оскільки вони також є в області модуля.

Якщо ви використовуєте TypeScript, також можна оголосити реквізити та випромінювачі за допомогою анотацій чистого типу.

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 } (посилання автоматично розгортаються, як і у звичайних екземплярах).

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 або спеціальні параметри, увімкнені через плагіни.
  • Декларування іменного експорту.
  • Запуск побічних ефектів або створення об'єктів, які повинні виконуватися лише один раз.
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, який наразі все ще є експериментальною функцією. Ми плануємо доопрацювати та задокументувати його в майбутньому випуску, але якщо вам зараз цікаво, ви можете переглянути його тести, щоб побачити, як це працює.

Можливості тільки для

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

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

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

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

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

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

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

    • Код, що видається, як і раніше, залишиться TypeScript з правильною типізацією, який може оброблятися іншими інструментами.

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

    • Літерал типу
    • Посилання на інтерфейс або літерал типу в тому самому файлі

    Наразі складні типи та імпорт типів з інших файлів не підтримуються. У майбутньому можна підтримувати імпорт типів.

Значення реквізитів за промовчанням під час використання об'яв типів

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

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

const props = withDefaults(defineProps<Props>(), {
  msg: 'hello',
  labels: () => ['one', 'two']
})

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

Обмеження

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

<script setup> has loaded