Skip to content

Commit 3e0cf97

Browse files
committed
feat: add migration guide > suspense
1 parent 1cc673a commit 3e0cf97

File tree

2 files changed

+108
-0
lines changed

2 files changed

+108
-0
lines changed

src/.vuepress/config.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@ const sidebar = {
131131
'migration/props-default-this',
132132
'migration/render-function-api',
133133
'migration/slots-unification',
134+
'migration/suspense',
134135
'migration/transition',
135136
'migration/v-if-v-for',
136137
'migration/v-model',

src/guide/migration/suspense.md

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
---
2+
badges:
3+
- new
4+
---
5+
6+
# Suspense <MigrationBadges :badges="$frontmatter.badges" />
7+
8+
:::warning Experimental
9+
Suspense is an experimental new feature and the API could change at any time. It is documented here so that the community can provide feedback on the current implementation.
10+
11+
It should not be used in production applications.
12+
:::
13+
14+
## Introduction
15+
16+
It is common for components to need to perform some kind of asynchronous request before they can be rendered properly. Components often handle this locally and in many cases that is a perfectly good approach.
17+
18+
The `<suspense>` component provides an alternative, allowing for the waiting to be handled further up the component tree rather than in each individual component.
19+
20+
A common use case involves [async components](/guide/component-dynamic-async.html#async-components):
21+
22+
```vue{2-4,6,17}
23+
<template>
24+
<suspense>
25+
<template #default>
26+
<todo-list />
27+
</template>
28+
<template #fallback>
29+
<div>
30+
Loading...
31+
</div>
32+
</template>
33+
</suspense>
34+
</template>
35+
36+
<script>
37+
export default {
38+
components: {
39+
TodoList: defineAsyncComponent(() => import('./TodoList.vue'))
40+
}
41+
}
42+
</script>
43+
```
44+
45+
The `<suspense>` component has two slots. Both slots only allow for one immediate child node. The node in the `default` slot is shown if possible. If not, the node in the `fallback` slot will be shown instead.
46+
47+
Importantly, the async component doesn't need to be the immediate child of the `<suspense>`. It can be at any depth within the component tree and doesn't need to appear in the same template as the `<suspense>` itself. The content is only considered resolved once all descendants are ready.
48+
49+
The other way to trigger the `fallback` slot is for a descendant component to return a promise from its `setup` function. This is typically implemented using `async` rather than explicitly returning a promise:
50+
51+
```js{2}
52+
export default {
53+
async setup() {
54+
// Be very careful using `await` inside `setup` as
55+
// most Composition API functions will only work
56+
// prior to the first `await`
57+
const data = await loadData()
58+
59+
// This is implicitly wrapped in a promise because
60+
// the function is `async`
61+
return {
62+
// ...
63+
}
64+
}
65+
}
66+
```
67+
68+
## Child Updates
69+
70+
Once a `<suspense>` has resolved the contents of its `default` slot, it can only be triggered again if the `default` root node is replaced. New components nested deeper in the tree are not sufficient to move the `<suspense>` back into a pending state.
71+
72+
If the root node does change it will trigger the `pending` event. However, by default, it won't update the DOM to show the `fallback` content. Instead, it will continue to show the old DOM until the new components are ready. This can be controlled using the `timeout` prop. This value, expressed in milliseconds, tells the `<suspense>` component how long to wait before showing the `fallback`. A value of `0` will show it immediately when the `<suspense>` enters the pending state.
73+
74+
## Events
75+
76+
In addition to the `pending` event, the `<suspense>` component also has `resolve` and `fallback` events. The `resolve` event is emitted when new content has finished resolving in the `default` slot. The `fallback` event is fired when the contents of the `fallback` slot are shown.
77+
78+
The events could be used, for example, to show a loading indicator in front of the old DOM while new components are loading.
79+
80+
## Combining with Other Components
81+
82+
It is common to want to use `<suspense>` in combination with the [`<transition>`](/api/built-in-components.html#transition) and [`<keep-alive>`](/api/built-in-components.html#keep-alive) components. The nesting order of these components is important to get them all working correctly.
83+
84+
In addition, these components are often used in conjunction with the `<router-view>` component from [Vue Router](https://next.router.vuejs.org/).
85+
86+
The following example shows how to nest these components so that they all behave as expected. For simpler combinations you can remove the components that you don't need:
87+
88+
```html
89+
<router-view v-slot="{ Component }">
90+
<template v-if="Component">
91+
<transition mode="out-in">
92+
<keep-alive>
93+
<suspense>
94+
<component :is="Component"></component>
95+
<template #fallback>
96+
<div>
97+
Loading...
98+
</div>
99+
</template>
100+
</suspense>
101+
</keep-alive>
102+
</transition>
103+
</template>
104+
</router-view>
105+
```
106+
107+
Vue Router has built-in support for [lazily loading components](https://next.router.vuejs.org/guide/advanced/lazy-loading.html) using dynamic imports. These are distinct from async components and currently they will not trigger `<suspense>`. However, they can still have async components as descendants and those can trigger `<suspense>` in the usual way.

0 commit comments

Comments
 (0)