Suspense
Експериментальна функція
<Suspense>
є експериментальною функцією. Немає гарантії, що вона досягне стабільного статусу, а її API може змінитися до цього.
<Suspense>
— це вбудований компонент для організації асинхронних залежностей у дереві компонентів. Він може візуалізувати стан завантаження, очікуючи вирішення кількох вкладених асинхронних залежностей у дереві компонентів.
Асинхронні залежності
Щоб пояснити проблему, яку намагається вирішити <Suspense>
, і як він взаємодіє з цими асинхронними залежностями, уявімо таку ієрархію компонентів:
<Suspense>
└─ <Dashboard>
├─ <Profile>
│ └─ <FriendStatus> (компонент з async setup())
└─ <Content>
├─ <ActivityFeed> (асинхронний компонент)
└─ <Stats> (асинхронний компонент)
У дереві компонентів є кілька вкладених компонентів, рендеринг яких залежить від деякого асинхронного ресурсу, який потрібно виконати першим. Без <Suspense>
кожному з них потрібно буде обробляти власне завантаження/помилку та завантажений стан. У гіршому випадку ми можемо побачити три завантажувальні обгортки на сторінці, вміст яких відображається в різний час.
Компонент <Suspense>
дає нам можливість відображати стани завантаження/помилки верхнього рівня, поки ми чекаємо вирішення цих вкладених асинхронних залежностей.
Існує два типи асинхронних залежностей, на які <Suspense>
може чекати:
Компоненти з асинхронним хуком
setup()
. Це включає компоненти, які використовують<script setup>
з виразамиawait
верхнього рівня.
async setup()
Хук setup()
компонента композиційного АРІ може бути асинхронним:
js
export default {
async setup() {
const res = await fetch(...)
const posts = await res.json()
return {
posts
}
}
}
Якщо використовується <script setup>
, наявність виразів await
верхнього рівня автоматично робить компонент асинхронною залежністю:
vue
<script setup>
const res = await fetch(...)
const posts = await res.json()
</script>
<template>
{{ posts }}
</template>
Асинхронні компоненти
Асинхроний компонент за промовчанням є "підвішеним". Це означає, що якщо він має <Suspense>
у батьківському ланцюгу, він буде розглядатися як асинхронна залежність цього <Suspense>
. У цьому випадку стан завантаження контролюватиметься <Suspense>
, а власні параметри завантаження, помилки, затримки та часу очікування компонента ігноруватимуться.
Асинхронний компонент може відмовитися від керування Suspense
і дозволити компоненту завжди контролювати власний стан завантаження, вказавши suspensible: false
у своїх параметрах.
Стан завантаження
Компонент <Suspense>
має два слоти: #default
і #fallback
. Обидва слоти дозволяють лише один безпосередній дочірній вузол. Якщо можливо, буде показано вузол у слоті за промовчанням. Якщо ні, замість нього буде показано вузол у резервному слоті.
template
<Suspense>
<!-- компонент із вкладеними асинхронними залежностями -->
<Dashboard />
<!-- стан завантаження через слот #fallback -->
<template #fallback>
Завантаження...
</template>
</Suspense>
Під час початкового рендерингу <Suspense>
відтворить вміст слота за промовчанням у пам’яті. Якщо під час процесу буде виявлено будь-які асинхронні залежності, він перейде в стан очікування. У стані очікування відображатиметься резервний вміст. Коли всі виявлені асинхронні залежності вирішено, <Suspense>
переходить у стан вирішено, і відображається вирішений вміст слота за промовчанням.
Якщо під час початкового рендерингу не виявлено асинхронних залежностей, <Suspense>
безпосередньо перейде в розв’язаний стан.
Перебуваючи у вирішеному стані, <Suspense>
повернеться до стану очікування, лише якщо буде замінено кореневий вузол слота #default
. Нові асинхронні залежності, вкладені глибше в дерево, не призведуть до того, що <Suspense>
повернеться до стану очікування.
Коли відбувається повернення, резервний вміст не відображатиметься одразу. Натомість <Suspense>
відображатиме попередній вміст #default
, очікуючи, поки буде вирішено новий вміст і його асинхронні залежності. Цю поведінку можна налаштувати за допомогою параметра timeout
: <Suspense>
переключиться на резервний вміст, якщо відтворення нового вмісту за промовчанням займає більше часу, ніж timeout
. Значення timeout
, що дорівнює 0, призведе до того, що резервний вміст буде відображено негайно після заміни вмісту за промовчанням.
Події
Компонент <Suspense>
випромінює 3 події: pending
, resolve
і fallback
. Подія pending
виникає під час входу в стан очікування. Подія resolve
запускається, коли новий вміст закінчує розпізнавання у слоті default
. Подія fallback
запускається, коли відображається вміст слота fallback
.
Події можна використовувати, наприклад, для показу індикатора завантаження перед старим DOM під час завантаження нових компонентів.
Обробка помилок
<Suspense>
наразі не забезпечує обробку помилок через сам компонент, однак ви можете використовувати параметр errorCaptured
або onErrorCaptured()
перехоплення для захоплення та обробки асинхронних помилок у батьківському компоненті <Suspense>
.
Поєднання з іншими компонентами
Часто бажають використовувати <Suspense>
у поєднанні з компонентами <Transition>
та <KeepAlive>
. Порядок вкладеності цих компонентів важливий для їхньої правильної роботи.
Крім того, ці компоненти часто використовуються в поєднанні з компонентом <RouterView>
з Vue Router.
У наступному прикладі показано, як вкладати ці компоненти, щоб усі вони вели себе належним чином. Для більш простих комбінацій ви можете видалити компоненти, які вам не потрібні:
template
<RouterView v-slot="{ Component }">
<template v-if="Component">
<Transition mode="out-in">
<KeepAlive>
<Suspense>
<!-- основний зміст -->
<component :is="Component"></component>
<!-- стан завантаження -->
<template #fallback>
Завантаження...
</template>
</Suspense>
</KeepAlive>
</Transition>
</template>
</RouterView>
Vue Router має вбудовану підтримку лінивого завантажування компонентів за допомогою динамічного імпорту. Вони відрізняються від асинхронних компонентів і наразі не запускають <Suspense>
. Однак вони все ще можуть мати асинхронні компоненти як нащадки, і вони можуть запускати <Suspense>
звичайним способом.