Skip to content

Commit 5fe8e0d

Browse files
docs: add an introduction to suspense to the migration guide (#912)
* docs: add an introduction to suspense to the migration guide * fix: be more explicit that router-view is from Vue Router
1 parent 3e876a7 commit 5fe8e0d

File tree

3 files changed

+109
-0
lines changed

3 files changed

+109
-0
lines changed

src/.vuepress/config.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,7 @@ const sidebar = {
198198
'/guide/migration/props-default-this',
199199
'/guide/migration/render-function-api',
200200
'/guide/migration/slots-unification',
201+
'/guide/migration/suspense',
201202
'/guide/migration/transition',
202203
'/guide/migration/transition-group',
203204
'/guide/migration/v-on-native-modifier-removed',

src/guide/migration/introduction.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ Some of the new features to keep an eye on in Vue 3 include:
4949
- [SFC Composition API Syntax Sugar (`<script setup>`)](https://github.com/vuejs/rfcs/blob/sfc-improvements/active-rfcs/0000-sfc-script-setup.md) <Badge text="experimental" type="warning" />
5050
- [SFC State-driven CSS Variables (`v-bind` in `<style>`)](https://github.com/vuejs/rfcs/blob/style-vars-2/active-rfcs/0000-sfc-style-variables.md) <Badge text="experimental" type="warning" />
5151
- [SFC `<style scoped>` can now include global rules or rules that target only slotted content](https://github.com/vuejs/rfcs/blob/master/active-rfcs/0023-scoped-styles-changes.md)
52+
- [Suspense](/guide/migration/suspense.html) <Badge text="experimental" type="warning" />
5253

5354
## Breaking Changes
5455

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)