# Добавлен компонент 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>
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 {
// ...
}
}
}
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>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Vue Router имеет встроенную поддержку для ленивой загрузки компонентов (opens new window) с помощью динамических импортов. Они отличаются от асинхронных компонентов и в настоящее время не вызывают <suspense>
. Но они могут иметь потомков в виде асинхронных компонентов, и те смогут вызывать <suspense>
как обычно.