<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
.