Vue 3 引入的 Suspense
组件用于处理异步组件的加载状态,使得开发者可以更优雅地处理组件加载时的等待状态和展示过渡效果。它主要用于异步组件和异步数据的获取,提供了一种新的方式来定义组件的加载状态,直到它的依赖项被解析完成。
Suspense 的作用
- 等待异步组件加载:当使用异步组件时,Suspense 允许你定义一个加载状态,直到该组件可用。
- 异步数据获取:对于需要异步获取数据的组件,Suspense 可以等待数据加载完成,然后再渲染组件。
- 代码分割:配合 Vue 3 的异步组件和 Webpack 的代码分割功能,Suspense 可以实现更加高效的代码加载,减少初始加载时间。
- 更好的用户体验:通过定义加载状态和错误处理,Suspense 提供了更平滑的用户体验,避免了加载过程中的空白或闪烁问题。
使用场景
- 懒加载组件:在应用中使用懒加载技术时,Suspense 可以提供一个加载指示器,告诉用户内容正在加载中。
- 数据依赖的组件渲染:对于依赖于异步数据的组件,可以使用 Suspense 包裹这些组件,直到所有需要的数据加载完成后再渲染。
- 路由懒加载:在 Vue 路由配置中,可以结合 Suspense 使用,实现路由级别的懒加载,优化应用的启动速度。
- 动态导入(Code Splitting):当使用动态
import()
语法导入组件时,Suspense 可以处理组件加载过程中的各种状态,提升应用性能。
基本用法
Vue 3 中使用 Suspense 通常涉及以下步骤:
- 使用
<Suspense>
包裹组件。 - 在
<template #default>
中放置异步组件或依赖异步数据的组件。 - 使用
<template #fallback>
定义加载状态下的展示内容。
<Suspense>
组件有两个插槽:#default
和 #fallback
。两个插槽都只允许一个直接子节点。在可能的时候都将显示默认槽中的节点。否则将显示后备槽中的节点。
<Suspense>
<!-- 具有深层异步依赖的组件 -->
<Dashboard />
<!-- 在 #fallback 插槽中显示 “正在加载中” -->
<template #fallback>
Loading...
</template>
</Suspense>
在初始渲染时,<Suspense>
将在内存中渲染其默认的插槽内容。如果在这个过程中遇到任何异步依赖,则会进入挂起状态。在挂起状态期间,展示的是后备内容。当所有遇到的异步依赖都完成后,<Suspense>
会进入完成状态,并将展示出默认插槽的内容。
如果在初次渲染时没有遇到异步依赖,<Suspense>
会直接进入完成状态。
进入完成状态后,只有当默认插槽的根节点被替换时,<Suspense>
才会回到挂起状态。组件树中新的更深层次的异步依赖不会造成 <Suspense>
回退到挂起状态。
发生回退时,后备内容不会立即展示出来。相反,<Suspense>
在等待新内容和异步依赖完成时,会展示之前 #default
插槽的内容。这个行为可以通过一个 timeout
prop 进行配置:在等待渲染新内容耗时超过 timeout
之后,<Suspense>
将会切换为展示后备内容。若 timeout
值为 0
将导致在替换默认内容时立即显示后备内容。
<Suspense>
组件会触发三个事件:pending
、resolve
和 fallback
。pending
事件是在进入挂起状态时触发。resolve
事件是在 default
插槽完成获取新内容时触发。fallback
事件则是在 fallback
插槽的内容显示时触发。
和其他组件结合
我们常常会将 <Suspense>
和 <Transition>、<KeepAlive> 等组件结合。要保证这些组件都能正常工作,嵌套的顺序非常重要。
另外,这些组件都通常与 Vue Router 中的 <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>
Vue Router 使用动态导入对懒加载组件进行了内置支持。这些与异步组件不同,目前他们不会触发 <Suspense>
。但是,它们仍然可以有异步组件作为后代,这些组件可以照常触发 <Suspense>
。
Suspense搭配
async函数的setup
<template>
<div class="app">
<h3>我是App组件</h3>
<Suspense>
<template v-slot:default>
<Child />
</template>
<template v-slot:fallback>
<h3>稍等,加载中...</h3>
</template>
</Suspense>
</div>
</template>
<script>
// import Child from './components/Child'//静态引入
import { defineAsyncComponent } from "vue";
const Child = defineAsyncComponent(() => import("./components/Child")); //异步引入
export default {
name: "App",
components: { Child },
};
</script>
<style>
.app {
background-color: gray;
padding: 10px;
}
</style>
定义setup为async的组件Child.vue
<template>
<div class="child">
<h3>我是Child组件</h3>
{{ sum }}
</div>
</template>
<script>
import { ref } from "vue";
export default {
name: "Child",
async setup() {
let sum = ref(0);
let p = new Promise((resolve) => {
setTimeout(() => {
resolve({ sum });
}, 3000);
});
return await p;
},
};
</script>
<style>
.child {
background-color: skyblue;
padding: 10px;
}
</style>
三秒内显示fallback,之后显示child组件。