异步组件
什么是异步组件
在Vue中,当我们注册全局或局部组件时,它们都是同步地被“立即解析并加载”的。这意味着在我们的程序初始化时,所有组件都会通过网络被下载到内存中,并且在内存中占用一定的资源。预加载所有组件会将页面的初始加载时间和性能降低,尤其是在移动设备上。为了避免这种情况,Vue.js 提供了异步组件。
为什么要使用异步组件
异步组件可以将我们的组件分开打包,按需要加载,这样可以减轻初始页面加载时间和减少资源浪费。当我们需要和路由配合使用时,异步组件也能帮助我们实现按需加载和动态导入。这样可以在调用组件时再切换路由实现动态加载器组件,有助于提高应用程序的性能和响应速度。
定义异步组件
Vue3中使用defineAsyncComponent()
来定义异步组件,该API的入参是一个返回组件选项的函数,需要使用 () => import()
函数来导入组件。
defineAsyncComponent
方法接收一个返回 Promise 的加载函数。这个 Promise 的 resolve 回调方法应该在从服务器获得组件定义时调用。我们也可以调用 reject(reason) 表明加载失败。
import { defineAsyncComponent } from 'vue'
const AsyncComponent = defineAsyncComponent(() => {
return new Promise((resolve, reject) => {
// ...从服务器获取组件
resolve(/* 获取到的组件 */)
})
})
ES 模块动态导入也会返回一个 Promise,所以我们会将它和 defineAsyncComponent 搭配使用。类似 Vite 和 Webpack 这样的构建工具也支持此语法 (并且会将它们作为打包时的代码分割点),因此我们也可以用它来导入 Vue 单文件组件:
import { defineAsyncComponent } from 'vue'
const AsyncComponent = defineAsyncComponent(() =>
import('./components/MyComponent.vue')
)
异步组件的加载与错误状态
我们在进行异步操作时,不可避免地会涉及到网络加载慢和加载错误的情况,vue在设计defineAsyncComponent() 组件时也考虑到了这种情况,它为我们提供了两个配置项即:加载异步组件时使用的组件loadingComponent和加载失败后展示的组件errorComponent。我们需要创建两个自定义组件LoadingComponent.vue 和 ErrorComponent.vue组件,作为上面两个配置项使用
示例代码如下
const AsyncComp = defineAsyncComponent({
// 加载函数
loader: () => import('./MyComponent.vue'),
// 加载异步组件时使用的组件
loadingComponent: LoadingComponent,
// 展示加载组件前的延迟时间,默认为 200ms
delay: 200,
// 加载失败后展示的组件
errorComponent: ErrorComponent,
// 如果提供了一个 timeout 时间限制,并超时了
// 也会显示这里配置的报错组件,默认值是:Infinity
timeout: 3000
})
Suspense
介绍
Suspense是一个内置的组件,可以将子组件的加载状态统一管理,包括异步组件的加载状态。它可以在异步加载组件的时候有一个loading状态,等异步组件创建好之后,再显示组件。
Suspense 组件有两个插槽:#default 和 #fallback。两个插槽都只允许一个直接子节点。
在初始渲染时,Suspense 将在内存中渲染其默认的#default插槽内容。如果在这个过程中遇到任何异步依赖,则会进入挂起状态。在挂起状态期间,展示的是#fallback后备内容。当所有遇到的异步依赖都完成后,Suspense 会进入完成状态,并将展示出默认插槽#default的内容。
如果在初次渲染时没有遇到异步依赖,Suspense会直接进入完成状态。
进入完成状态后,只有当默认插槽#default的根节点被替换时,Suspense才会回到挂起状态。组件树中新的更深层次的异步依赖不会造成 Suspense回退到挂起状态。
发生回退时, #fallback后备内容不会立即展示出来。相反,Suspense在等待新内容和异步依赖完成时,会展示之前 #default 插槽的内容。这个行为可以通过一个 timeout prop 进行配置:在等待渲染新内容耗时超过 timeout 之后,Suspense 将会切换为展示 #fallback后备内容。若 timeout 值为 0 将导致在替换#default 默认内容时立即显示 #fallback后备内容。
事件
Suspense 组件会触发三个事件:pending、resolve 和 fallback。pending 事件是在进入挂起状态时触发。resolve 事件是在 default 插槽完成获取新内容时触发。fallback 事件则是在 fallback 插槽的内容显示时触发。
例如,可以使用这些事件在加载新组件时在之前的 DOM 最上层显示一个加载指示器。
错误处理
Suspense 组件自身目前还不提供错误处理,不过你可以使用 errorCaptured 选项或者 onErrorCaptured() 钩子,在使用到 Suspense 的父组件中捕获和处理异步错误。
和其他组件结合
我们常常会将 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>