Skip to content

Suspense

Експериментальна функція

<Suspense> є експериментальною функцією. Немає гарантії, що вона досягне стабільного статусу, а її API може змінитися до цього.

<Suspense> — це вбудований компонент для організації асинхронних залежностей у дереві компонентів. Він може візуалізувати стан завантаження, очікуючи вирішення кількох вкладених асинхронних залежностей у дереві компонентів.

Асинхронні залежності

Щоб пояснити проблему, яку намагається вирішити <Suspense>, і як він взаємодіє з цими асинхронними залежностями, уявімо таку ієрархію компонентів:

<Suspense>
└─ <Dashboard>
   ├─ <Profile>
   │  └─ <FriendStatus> (компонент з async setup())
   └─ <Content>
      ├─ <ActivityFeed> (асинхронний компонент)
      └─ <Stats> (асинхронний компонент)

У дереві компонентів є кілька вкладених компонентів, рендеринг яких залежить від деякого асинхронного ресурсу, який потрібно виконати першим. Без <Suspense> кожному з них потрібно буде обробляти власне завантаження/помилку та завантажений стан. У гіршому випадку ми можемо побачити три завантажувальні обгортки на сторінці, вміст яких відображається в різний час.

Компонент <Suspense> дає нам можливість відображати стани завантаження/помилки верхнього рівня, поки ми чекаємо вирішення цих вкладених асинхронних залежностей.

Існує два типи асинхронних залежностей, на які <Suspense> може чекати:

  1. Компоненти з асинхронним хуком setup(). Це включає компоненти, які використовують <script setup> з виразами await верхнього рівня.

  2. Асинхронні компоненти.

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> звичайним способом.

Suspense has loaded