Vue3.2 内置组件 Suspense
Suspense 组件
Suspense
是Vue3.2新增的内置组件,与React.Suspense
组件类似。
Suspense
有两个插槽:default
和fallback
default
插槽中存放具有深层异步依赖的组件/(异步组件)
当default
中的组件状态未变成fulfilled
(完成且未抛出异常)时,会显示fallback
插槽中的内容
1.配合defineAsyncComponent
懒加载组件
child component A
<!-- ./components/A.vue -->
<template>
<h1>hello</h1>
</template>
parent component
<script setup lang="ts">
import {defineAsyncComponent} from "vue";
const A = defineAsyncComponent(() => import('./components/A.vue'))
</script>
<template>
<Suspense>
<A/>
<template #fallback>
<p>loading...</p>
</template>
</Suspense>
</template>
2.使用async setup
child component B
<!-- ./components/B.vue -->
<script lang="ts" setup>
const getTitle = () => {
return new Promise<string>((resolve, reject) => {
setTimeout(() => {
resolve('hello')
// reject(new Error('wo wo wo ~'))
}, 3000)
})
}
// 直接在script setup 使用顶层await, vue 会使得 setup函数变成 async setup() {}
const title = await getTitle()
</script>
<template>
<h1>{{title}}</h1>
</template>
parent component
<script setup lang="ts">
import B from './components/B.vue'
</script>
<template>
<Suspense>
<B/>
<template #fallback>
<p>loading...</p>
</template>
</Suspense>
</template>
3.配合vueRouter使用
<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>
4.错误边界
<Suspense>
组件自身目前还不提供错误处理,不过可以使用errorCaptured
选项或者onErrorCaptured()
钩子,在使用到<Suspense>
的父组件中捕获和处理异步错误。
以 component B 为例 将getTitle
的resolve
改为 reject
- resolve('hello')
+ reject(new Error('wo wo wo ~'))
parent component
<script setup lang="ts">
import {defineAsyncComponent, onErrorCaptured, ref} from "vue";
const B = defineAsyncComponent(() => import('./components/B.vue'))
const errorMessage = ref<string | null>(null)
onErrorCaptured((e) => {
errorMessage.value = e instanceof Error ? e.message : (e || 'has error')
})
</script>
<template>
<Suspense>
<B/>
<template #fallback>
<p>{{ errorMessage || 'loading...' }}</p>
</template>
</Suspense>
</template>
为之则易,不为则难。