# Добавлен компонент suspense
новое

Экспериментальная возможность

Suspense — новая экспериментальная возможность и её API может измениться в любой момент. Она задокументирована здесь, чтобы сообщество могло оставлять свои отзывы о текущей реализации.

Она не должна использоваться в production приложениях.

# Введение

Как правило, компонентам нужно выполнить какой-либо асинхронный запрос, прежде чем смогут отобразиться корректным образом. Часто эта ситуация обрабатывается внутри компонентов локально, и во многих случаях такой подход вполне оправдан.

Компонент <suspense> предоставляет альтернативу, позволяя обрабатывать ситуацию с ожиданием данных дальше по дереву компонентов, а не в каждом отдельном компоненте.

Чаще всего подобное может пригодиться при использовании асинхронных компонентов:


 
 
 

 










 




<template>
  <suspense>
    <template #default>
      <todo-list />
    </template>
    <template #fallback>
      <div>
        Loading...
      </div>
    </template>
  </suspense>
</template>

<script>
export default {
  components: {
    TodoList: defineAsyncComponent(() => import('./TodoList.vue'))
  }
}
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

Компонент <suspense> предоставляет два слота. В каждом можно указать только по одному дочернему элементу. Элемент из слота default показывается, если это возможно. Если нет, то вместо него отображается элемент из слота fallback.

Важно отметить, асинхронный компонент не обязательно должен быть непосредственным потомком <suspense>. Он может находиться на любой глубине дерева компонентов и даже не обязательно должен быть в том же шаблоне, что и <suspense>. Содержимое считается разрешённым только тогда, когда все потомки будут готовы.

Другой способ задействования слота fallback заключается в том, чтобы компонент-потомок возвращал Promise из своей функции setup. Часто реализация может быть создана с использованием async, вместо явного возврата Promise:


 













export default {
  async setup() {
    // С осторожностью используйте `await` внутри `setup`,
    // потому что большинство функций Composition API
    // БУДУТ РАБОТАТЬ ТОЛЬКО ДО ПЕРВОГО `await`
    const data = await loadData()

    // Это будет неявно обёрнуто в Promise,
    // потому что функция является `async`
    return {
      // ...
    }
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# Обновления дочерних компонентов

После того, как <suspense> разрешает содержимое своего слота default, он может быть перезапущен снова, но только при замене корневого узла в default. Новых компонентов, вложенных глубже в дерево недостаточно, чтобы перевести <suspense> обратно в состояние ожидания.

Если корневой элемент изменится, то сгенерируется событие pending. Но, по умолчанию, оно не будет обновлять DOM, чтобы показать содержимое fallback. Вместо этого старый DOM продолжит отображаться до тех пор, пока не будут готовы новые компоненты. Этим можно управлять с помощью входного параметра timeout. Его значение в миллисекундах, указывает компоненту <suspense> сколько нужно ждать перед тем как показать fallback. При значении 0 оно будет показано сразу, как только <suspense> перейдёт в состояние ожидания.

# События

Компонент <suspense> кроме события pending также получает resolve и fallback. Событие resolve происходит, когда разрешилось новое содержимое в слоте default. Событие fallback происходит, когда отображается содержимое слота fallback.

Эти события можно использовать, например, для отображения индикатора загрузки перед старым DOM во время загрузки новых компонентов.

# Сочетание с другими компонентами

Иногда может потребоваться использовать <suspense> в сочетании с компонентами <transition> и <keep-alive>. Для этих компонентов важен порядок вложенности, чтобы все они смогли работать корректно.

Кроме того, они могут часто использоваться в сочетании с <router-view> из Vue Router (opens new window).

В следующем примере показано, как нужно вкладывать эти компоненты, чтобы все они работали так, как и ожидается. Для более простых комбинаций можно просто удалить ненужные компоненты:

<router-view v-slot="{ Component }">
  <template v-if="Component">
    <transition mode="out-in">
      <keep-alive>
        <suspense>
          <component :is="Component"></component>
          <template #fallback>
            <div>
              Загрузка...
            </div>
          </template>
        </suspense>
      </keep-alive>
    </transition>
  </template>
</router-view>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

Vue Router имеет встроенную поддержку для ленивой загрузки компонентов (opens new window) с помощью динамических импортов. Они отличаются от асинхронных компонентов и в настоящее время не вызывают <suspense>. Но они могут иметь потомков в виде асинхронных компонентов, и те смогут вызывать <suspense> как обычно.