目录
是什么
<Suspense>
是一个内置组件,用来在组件树中协调对异步依赖的处理。它让我们可以在组件树上层等待下层的多个嵌套异步依赖项解析完成,并可以在等待时渲染一个加载状态。通俗来讲就是用来处理异步组件加载时的 loading 状态的,等待组件异步加载,加载完成后统一显示。
一般组件中获取接口数据,使用v-if和v-else控制loading,当接口返回后把loading设为false,但是当页面接口过多时,到处都是 loading,为了协调整个页面的加载状态,包括深度嵌套的组件,<Suspense>
可以一次性加载所有东西。
如何使用
<Suspense>
<!-- 具有深层异步依赖的组件 -->
<Dashboard />
<!-- 在 #fallback 插槽中显示 “正在加载中” -->
<template #fallback>
Loading...
</template>
</Suspense>
异步组件是以下两种情况之一:
- 一个带有
async setup
函数的组件,该组件返回一个Promise,或者在script setup
中使用顶级await
- 使用
defineAsyncComponent
异步加载的组件
第一种
child.vue
<template>
<slot />
</template>
<script setup>
const { time, index } = defineProps({
time: {
type: Number,
required: true,
},
index: {
type: Number,
default: 1,
},
});
// 加入一个延迟,以模拟加载数据
await new Promise((resolve) => {
setTimeout(() => {
resolve();
console.log(`第${index}个加载完成`);
}, time);
});
</script>
<template>
<Suspense>
<!-- 具有深层异步依赖的组件 -->
<div>
<Child index={1} time={3000}>
第一个
</Child>
<Child index={2} time={2000}>
第二个
</Child>
<Child index={3} time={1000}>
第三个
<Child index={4} time={500}>
第四个
</Child>
<Child index={5} time={4000}>
第五个
</Child>
</Child>
</div>
<!-- 在 #fallback 插槽中显示 “正在加载中” -->
<template #fallback>
Loading...
</template>
</Suspense>
</template>
<script setup>
import Child from "./child.vue";
</script>
第二种
<template>
<slot />
</template>
<script setup>
const { time, index } = defineProps({
time: {
type: Number,
required: true,
},
index: {
type: Number,
default: 1,
},
});
setTimeout(() => {
console.log(`第${index}个加载完成`);
}, time);
</script>
<Suspense>
<div>
<Child index={1} time={3000}>
第一个
</Child>
<Child index={2} time={2000}>
第二个
</Child>
<Child index={3} time={1000}>
第三个
<Child index={4} time={500}>
第四个
</Child>
<Child index={5} time={4000}>
第五个
</Child>
</Child>
</div>
<!-- 在 #fallback 插槽中显示 “正在加载中” -->
<template #fallback>
Loading...
</template>
</Suspense>
<script setup>
import Child from "./child.vue";
</script>
<script setup>
import {defineAsyncComponent} from 'vue'
const Child = defineAsyncComponent(()=>import('./components/Child'))
</script>
异步瀑布
如果你仔细注意,你会注意到这些组件并不像你想象的那样是并联加载的。
总的加载时间不是基于最慢的组件(4秒)。相反,这个时间要长得多。这是因为Vue只有在父异步组件完全解析后才会开始加载子组件。
这里有一个深度嵌套的组件,它需要整整5秒来加载,阻塞了整个UI,尽管大多数组件加载完成的时间要早得多。
但对我们来说有一个解决方案
通过进一步嵌套第二个Suspense组件,我们可以在等待这个组件完成加载时显示应用程序的其他部分。
<template>
<Suspense>
<div>
<Child index={1} time={3000}>
第一个
</Child>
<Child index={2} time={2000}>
第二个
</Child>
<Suspense>
<div>
<Child index={4} time={500}>
第四个
</Child>
<Child index={5} time={4000}>
第五个
</Child>
</div>
<template #fallback>
Loading...
</template>
</Suspense>
</div>
<!-- 在 #fallback 插槽中显示 “正在加载中” -->
<template #fallback>
Loading...
</template>
</Suspense>
</template>
<script setup>
import Child from "./child.vue";
</script>
<script setup>
import {defineAsyncComponent} from 'vue'
const Child = defineAsyncComponent(()=>import('./components/Child'))
</script>
错误处理
<Suspense>
组件自身目前还不提供错误处理,不过你可以使用 errorCaptured 选项或者 onErrorCaptured() 钩子,在使用到 <Suspense>
的父组件中捕获和处理异步错误。
和其他组件结合
我们常常会将 <Suspense>
和<Transition>、<KeepAlive> 等组件结合。要保证这些组件都能正常工作,嵌套的顺序非常重要。
另外,这些组件都通常与 <RouterView>
组件结合使用。
下面的示例展示了如何嵌套这些组件,使它们都能按照预期的方式运行。若想组合得更简单,你也可以删除一些你不需要的组件:
<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>