当我们的项目达到一定的规模时,对于某些组件来说,我们并不希望一开始全部加载,而是需要的时候进行加载;这样的做得目的可以很好的提高用户体验。
为了实现这个功能,Vue3中为我们提供了一个方法,即defineAsyncComponent,这个方法可以传递两种类型的参数,分别是函数类型和对象类型,接下来我们分别学习。
传递工厂函数作为参数
defineAsyncComponent方法接收一个工厂函数是它的基本用法,这个工厂函数必须返回一个Promise,Promise的resolve应该返回一个组件。
我们这里以Vue Cli创建的项目为例,这里我稍微做了一下修改,将头部的图片拆分为一个组件,代码如下:
<template>
<logo-img />
<hello-world msg="Welcome to Your Vue.js App" />
</template>
<script setup>
import LogoImg from './components/LogoImg.vue'
import HelloWorld from './components/HelloWorld.vue'
</script>
现在我们就将<hello-world>组件修改为异步组件,示例代码如下:
<template>
<logo-img />
<hello-world msg="Welcome to Your Vue.js App" />
</template>
<script setup>
import { defineAsyncComponent } from 'vue'
import LogoImg from './components/LogoImg.vue'
// 简单用法
const HelloWorld = defineAsyncComponent(() =>
import('./components/HelloWorld.vue'),
)
</script>
我们这里为了看到效果,将import延迟执行,示例代码如下:
<script setup>
import { defineAsyncComponent } from 'vue'
import LogoImg from './components/LogoImg.vue'
// 定义一个耗时执行的函数,t 表示延迟的时间, callback 表示需要执行的函数,可选
const time = (t, callback = () => {}) => {
return new Promise(resolve => {
setTimeout(() => {
callback()
resolve()
}, t)
})
}
// 定义异步组件,这里这样写是为了查看效果
const HelloWorld = defineAsyncComponent(() => {
return new Promise((resolve, reject) => {
;(async function () {
try {
await time(2000)
const res = await import('./components/HelloWorld.vue')
resolve(res)
} catch (error) {
reject(error)
}
})()
})
})
</script>
代码运行结果如下所示:

当2s后才会加载<hello-world>组件。
传递对象类型作为参数
defineAsyncComponent方法也可以接收一个对象作为参数,该对象中有如下几个参数:
loader:同工厂函数;loadingComponent:加载异步组件时展示的组件;errorComponent:加载组件失败时展示的组件;delay:显示loadingComponent之前的延迟时间,单位毫秒,默认200毫秒;timeout:如果提供了timeout,并且加载组件的时间超过了设定值,将显示错误组件,默认值为Infinity(单位毫秒);suspensible:异步组件可以退出<Suspense>控制,并始终控制自己的加载状态。onError:一个函数,该函数包含4个参数,分别是error、retry、fail和attempts,这4个参数分别是错误对象、重新加载的函数、加载程序结束的函数、已经重试的次数。
如下代码展示defineAsyncComponent方法的对象类型参数的用法:
<template>
<logo-img />
<hello-world msg="Welcome to Your Vue.js App" />
</template>
<script setup>
import { defineAsyncComponent } from 'vue'
import LogoImg from './components/LogoImg.vue'
import LoadingComponent from './components/loading.vue'
import ErrorComponent from './components/error.vue'
// 定义一个耗时执行的函数,t 表示延迟的时间, callback 表示需要执行的函数,可选
const time = (t, callback = () => {}) => {
return new Promise(resolve => {
setTimeout(() => {
callback()
resolve()
}, t)
})
}
// 记录加载次数
let count = 0
const HelloWorld = defineAsyncComponent({
// 工厂函数
loader: () => {
return new Promise((resolve, reject) => {
;(async function () {
await time(300)
const res = await import('./components/HelloWorld.vue')
if (++count < 3) {
// 前两次加载手动设置加载失败
reject(res)
} else {
// 大于3次成功
resolve(res)
}
})()
})
},
loadingComponent: LoadingComponent,
errorComponent: ErrorComponent,
delay: 0,
timeout: 1000,
suspensible: false,
onError(error, retry, fail, attempts) {
// 注意,retry/fail 就像 promise 的 resolve/reject 一样:
// 必须调用其中一个才能继续错误处理。
if (attempts < 3) {
// 请求发生错误时重试,最多可尝试 3 次
console.log(attempts)
retry()
} else {
fail()
}
},
})
</script>
上面的代码中,我们加载组件时前两次会请求错误,只有第三次加载才会成功,代码运行结果如下:

如果加载失败则会展示ErrorComponent组件。
常见考点
1. 基本概念与使用方式
问题:
- 什么是 Vue 中的异步组件?你如何在 Vue 中定义异步组件?
- 使用异步组件有什么好处?
考察点:
- 基本概念: 异步组件是一种按需加载的组件。在 Vue 中,异步组件通常是通过动态导入语法(
import())来实现的,只有在该组件被实际需要渲染时才会被加载。 - 定义异步组件: 使用
Vue.component或在路由配置中通过component定义异步组件,通常是通过返回一个动态导入的 Promise 来实现:
const AsyncComponent = () => import('./AsyncComponent.vue');
- 示例:
const AsyncComponent = () => import('./AsyncComponent.vue');
export default {
components: {
AsyncComponent
}
}
- 好处:
- 减少首屏加载时间:将部分不必要的组件推迟加载,有助于提升首屏渲染速度。
- 按需加载:用户访问不同的页面时,只有在需要时才加载对应的组件,避免一次性加载过多内容。
- 提升性能:通过懒加载大组件,减少初始加载的资源大小,提升用户体验。
2. 异步组件的加载过程
问题:
- Vue 中的异步组件是如何工作的?它如何实现懒加载?
- 在使用异步组件时,如果加载失败,Vue 会如何处理?
考察点:
- 工作原理: 当页面渲染到异步组件时,Vue 会触发该组件的动态导入。这个操作是通过
import()实现的,它返回一个Promise,当这个Promise被解析时,组件会被加载并渲染。import()返回的Promise会在加载完成后解析,解析结果就是加载的组件,Vue 会在该组件需要渲染时进行加载。
const AsyncComponent = () => import('./AsyncComponent.vue');
- 加载失败的处理: 如果异步组件的加载失败,通常可以通过
Promise的catch方法来处理错误。Vue 提供了error选项来捕获加载失败的情况,并可以展示一个备用组件或提示信息。 另外,Vue 提供了loading和error属性用于处理加载过程中和加载失败时的状态:
const AsyncComponent = () => import('./AsyncComponent.vue').catch(() => import('./ErrorComponent.vue'));
const AsyncComponent = () => ({
component: import('./AsyncComponent.vue'),
loading: LoadingComponent,
error: ErrorComponent,
delay: 200, // 延迟200ms后显示 loading 组件
timeout: 3000 // 超过3秒还没加载完成显示 error 组件
});
3. 异步组件的性能优化
问题:
- 如何优化异步组件的加载性能?可以使用哪些策略?
- 在多个异步组件的情况下,如何确保它们高效加载?
考察点:
- 懒加载与拆分: 通过按需加载和拆分大型组件,可以减少初始页面加载的资源量。例如,对于一个大页面,可以把页面拆分成多个异步组件,只有在需要渲染时才加载。
- 缓存与预加载: 如果你知道某个组件即将被使用,可以使用 Vue 的
preload或prefetch来提前加载。prefetch是一种告诉浏览器在空闲时加载资源的策略,这有助于提升组件加载的速度。
const AsyncComponent = () => ({
component: import('./AsyncComponent.vue'),
loading: LoadingComponent,
error: ErrorComponent,
delay: 200,
timeout: 3000,
prefetch: true // 预加载
});
- 使用 Webpack 的代码分割: 使用 Webpack 时,动态导入会自动分割代码,使得组件在需要时才会被加载到浏览器中。通过设置合理的 chunk 配置,可以更好地管理资源加载。 这会将该组件单独分割为一个
async-component.js文件。
const AsyncComponent = () => import(/* webpackChunkName: "async-component" */ './AsyncComponent.vue');
- 异步组件与路由懒加载结合: Vue Router 支持懒加载路由组件,可以与异步组件结合,在路由切换时按需加载组件。
const routes = [
{
path: '/about',
component: () => import('./About.vue') // 异步加载 About 组件
}
];
4. 与 Vue Router 的结合
问题:
- 在 Vue Router 中,如何使用异步组件?
- 路由懒加载与异步组件的关系是什么?
考察点:
- 路由懒加载: 在 Vue Router 中,异步组件通常与路由懒加载一起使用。通过动态导入的方式,当用户访问某个路由时,只有相关的组件才会被加载。
const routes = [
{
path: '/home',
component: () => import('./Home.vue') // 路由懒加载
}
];
- 加载提示: 可以在路由懒加载的过程中添加加载提示,常见的做法是在加载时展示一个 loading 组件,加载完成后展示目标组件。
5. 与其他异步机制的区别
问题:
- 异步组件和
Promise、setTimeout等异步机制有什么区别?为什么import()会作为异步组件的实现方式? - 你如何选择使用异步组件、动态导入、
Promise或其他异步机制?
考察点:
import()与其他异步机制:import()是 ES6 的语法,返回一个Promise,它支持模块化的按需加载,并且是静态导入的延伸。- 相较于
setTimeout等传统异步机制,import()具有更高的语义清晰度,表示按需加载模块,并且与 JavaScript 的模块系统兼容。 - 选择异步机制:
- 对于大部分组件,尤其是大型应用中的部分组件,使用异步组件的方式可以显著提升页面性能。
- 对于一些小的、需要提前加载的组件,使用普通的同步加载即可。
总结
异步组件在 Vue 中是非常有用的优化工具,允许按需加载组件,从而显著提高应用性能。通过结合动态导入、Vue Router 的懒加载和合适的预加载策略,可以实现高效的资源管理和加载过程。在面试中,关于异步组件的考察通常会涉及其工作原理、加载失败处理、性能优化策略,以及与 Vue Router 的结合使用等方面。