vue3 + vite + ts + setup , 第四练 异步组件的使用,defineAsyncComponent和Suspense的使用
vue3+vite+ts 异步组件的使用,defineAsyncComponent和Suspense的使用
在大型项目中,我们可能需要拆分应用为更小的块,并仅在需要时再从服务器加载相关组件。这样做的目的可以提高页面加载速度,优化用户体验,减少首屏白屏时间
defineAsyncComponent
方法接收一个返回 Promise 的加载函数。这个 Promise 的 resolve
回调方法应该在从服务器获得组件定义时调用。你也可以调用 reject(reason)
表明加载失败。
defineAsyncComponent单独使用
import { defineAsyncComponent } from 'vue'
const AsyncComp = defineAsyncComponent(() =>
import('./components/MyComponent.vue')
)
异步组件简单用法:只有在使用的时候才会加载
<template>
<div class="content">
<asyncCom></asyncCom>
</div>
</template>
<script setup lang="ts">
import { defineAsyncComponent, markRaw, reactive, ref, Ref } from 'vue';
let asyncCom = defineAsyncComponent(() => import('./asyncCom.vue'))
</script>
复杂用用法:
const AsyncComp = defineAsyncComponent({
// 加载函数
loader: () => import('./Foo.vue'),
// 加载异步组件时使用的组件
loadingComponent: LoadingComponent,
// 展示加载组件前的延迟时间,默认为 200ms
delay: 200,
// 加载失败后展示的组件
errorComponent: ErrorComponent,
// 如果提供了一个 timeout 时间限制,并超时了
// 也会显示这里配置的报错组件,默认值是:Infinity
timeout: 3000
})
传递对象类型作为参数
defineAsyncComponent方法也可以接收一个对象作为参数,该对象中有如下几个参数:loader:同工厂函数;
loadingComponent:加载异步组件时展示的组件;
errorComponent:加载组件失败时展示的组件;
delay:显示loadingComponent之前的延迟时间,单位毫秒,默认200毫秒;
timeout:如果提供了timeout,并且加载组件的时间超过了设定值,将显示错误组件,默认值为Infinity(单位毫秒);
suspensible:异步组件可以退出<Suspense>控制,并始终控制自己的加载状态。具体可以参考文档;
onError:一个函数,该函数包含4个参数,分别是error、retry、fail和attempts,这4个参数分别是错误对象、重新加载的函数、加载程序结束的函数、已经重试的次数。
复杂用法举例:正常加载的话会很快,我们看不到效果,所以做了延时加载
<template>
<div class="content">
<div class="async-component">
<button @click="isShow = true">clickccc</button>
<asyncCom v-if="isShow"></asyncCom>
</div>
</div>
</template>
<script setup lang="ts">
import { defineAsyncComponent, markRaw, reactive, ref, Ref } from 'vue';
import cpA from './cpA.vue';
import cpB from './cpB.vue';
import cpC from './cpC.vue';
let num = 0
let asyncCom = defineAsyncComponent({
loader: () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
let res: any = import('./asyncCom.vue')
num++
console.log("numsssss", num)
if (num < 3) {
reject(res)
} else {
resolve(res)
}
}, 300)
})
},
loadingComponent: cpB,
delay: 100,
errorComponent: cpC,
timeout: 5000,
suspensible: true,
onError(error, retry, fail, attempts) {
// 注意,retry/fail 就像 promise 的 resolve/reject 一样:
// 必须调用其中一个才能继续错误处理。
if (attempts < 3) {
// 请求发生错误时重试,最多可尝试 3 次
console.log("qingqiu ", attempts)
retry()
} else {
fail()
}
}
})
let isShow: Ref<Boolean> = ref(false)
</script>
上面代码做了前两次会请求错误,第三次会请求成功,下面是效果:
修改代码attempts判断2次就抛出错误,然后加载errorComponent组件 , 模拟组件加载错误显示组件
let num = 0
let asyncCom = defineAsyncComponent({
loader: () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
let res: any = import('./asyncCom.vue')
num++
console.log("numsssss", num)
if (num < 3) {
reject(res)
} else {
resolve(res)
}
}, 300)
})
},
loadingComponent: cpB,
delay: 100,
errorComponent: cpC,
timeout: 5000,
suspensible: true,
onError(error, retry, fail, attempts) {
// 注意,retry/fail 就像 promise 的 resolve/reject 一样:
// 必须调用其中一个才能继续错误处理。
if (attempts < 2) {
// 请求发生错误时重试,最多可尝试 3 次
console.log("qingqiu ", attempts)
retry()
} else {
fail()
}
}
})
2次请求错误后抛出错误,显示错误组件效果如下:
以上是defineAsyncComponent单独使用;
defineAsyncComponent 搭配 Suspense 使用
异步依赖#
要了解 <Suspense>
所解决的问题和它是如何与异步依赖进行交互的,我们需要想象这样一种组件层级结构:
<Suspense>
└─ <Dashboard>
├─ <Profile>
│ └─ <FriendStatus>(组件有异步的 setup())
└─ <Content>
├─ <ActivityFeed> (异步组件)
└─ <Stats>(异步组件)
在这个组件树中有多个嵌套组件,要渲染出它们,首先得解析一些异步资源。如果没有 <Suspense>
,则它们每个都需要处理自己的加载、报错和完成状态。在最坏的情况下,我们可能会在页面上看到三个旋转的加载态,在不同的时间显示出内容。
有了 <Suspense>
组件后,我们就可以在等待整个多层级组件树中的各个异步依赖获取结果时,在顶层展示出加载中或加载失败的状态。
<Suspense>
可以等待的异步依赖有两种:
-
带有异步
setup()
钩子的组件。这也包含了使用<script setup>
时有顶层await
表达式的组件。 -
异步组件。
组合式 API 中组件的 setup()
钩子可以是异步的
export default {
async setup() {
const res = await fetch(...)
const posts = await res.json()
return {
posts
}
}
}
<script setup>
,那么顶层 await
表达式会自动让该组件成为一个异步依赖:
<script setup>
const res = await fetch(...)
const posts = await res.json()
</script>
第一步:改写异步组件 asyncCom.vue
<template>
<div>
我是异步组件{{resdata}}
</div>
</template>
<script setup lang="ts">
import { ref } from "vue";
let resdata = ref<any>('')
let testFun = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log("1313")
resolve("SUCCESS")
}, 1000)
})
}
resdata = await testFun()
</script>
<style scoped>
</style>
第二步:父组件引用
<template>
<div class="content">
<div class="async-component">
<button @click="isShow = true">clickccc</button>
<Suspense v-if="isShow">
<template #default>
<asyncCom />
</template>
<template #fallback>
<p> Loading... </p>
</template>
</Suspense>
</div>
</div>
</template>
<script setup lang="ts">
import { defineAsyncComponent, markRaw, reactive, ref, Ref } from 'vue';
let asyncCom = defineAsyncComponent(()=>import('./asyncCom.vue'))
let isShow: Ref<Boolean> = ref(false)
</script>
<suspense>
组件有两个插槽。它们都只接收一个直接子节点。default
插槽里的节点会尽可能展示出来。如果不能,则展示 fallback
插槽里的节点。上面代码显示:首先加载组件 asyncCom,由于组件异步加载数据,所以此时会显示#fallback中内容,等待组件asyncCom加载完成功后显示asyncCom,页面效果如下:
和其他组件结合,嵌套顺序
我们常常会将 <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>
更多推荐
所有评论(0)