defineAsyncComponent 用来定义异步组件。
2025-11-23 16:04:26
defineAsyncComponent 用来定义异步组件。
不会在初始化的时候立即加载, 而是在渲染到页面时, 进行异步加载。
这样的话就可以将组件拆分为独立的代码块,减少首屏需要加载的资源
使用方式
- 最简单的方式是直接传入一个异步组件加载函数:
const AsyncComp = defineAsyncComponent(() => import('./Comp.vue'))
- 也可以传入一个配置对象,在加载的同时,指定加载中和加载失败时显示的组件:
const AsyncComp = defineAsyncComponent({
loader: () => import('./Comp.vue'),
loadingComponent: LoadingComponent,
errorComponent: ErrorComponent,
timeout: 3000
})
实现
1. 基础结构
defineAsyncComponent 的类型定义:
function defineAsyncComponent(
source: AsyncComponentLoader | AsyncComponentOptions
): Component
type AsyncComponentLoader = () => Promise<Component>
interface AsyncComponentOptions {
loader: AsyncComponentLoader
loadingComponent?: Component
errorComponent?: Component
delay?: number
timeout?: number
suspensible?: boolean
onError?: (
error: Error,
retry: () => void,
fail: () => void,
attempts: number
) => any
}
defineAsyncComponent 既可以接收加载函数,也可以接收配置对象。
为了统一处理,可以先把函数形式转换成配置对象:
import { isFunction } from '@vue/shared'
export function defineAsyncComponent(options) {
if (isFunction(options)) {
options = {
loader: options
}
}
const { loader } = options
const component = ref(() => h('span', null, ''))
return {
setup() {
loader().then((comp) => {
if (comp && comp[Symbol.toStringTag] === 'Module') {
comp = comp.default
}
component.value = comp
})
return () => h(component.value)
}
}
}
- 通过响应式变量
component保存当前正在渲染的组件 - 初始化时先渲染一个空的
span, 在加载完成后, 将异步组件保存到component上 - 对于使用
import()动态导入的组件, 返回值是模块对象, 取对应的default
2. 支持 loadingComponent 和 errorComponent
当前实现只在加载完成后渲染组件, 加载过程中和加载失败时没有对应 UI
import { isFunction } from '@vue/shared'
export function defineAsyncComponent(options) {
if (isFunction(options)) {
options = {
loader: options
}
}
const defaultComponent = () => h('span', null, '')
const {
loader,
loadingComponent = defaultComponent,
errorComponent = defaultComponent
} = options
const component = ref(loadingComponent)
return {
setup() {
loader().then(
(comp) => {
if (comp && comp[Symbol.toStringTag] === 'Module') {
comp = comp.default
}
component.value = comp
},
() => {
component.value = errorComponent
}
)
return () => h(component.value)
}
}
}
初始化时先渲染 loadingComponent。
加载成功后,切换为异步组件;加载失败后,切换为 errorComponent。
3. 支持 timeout
timeout 用来限制异步组件的加载时间。
把原来的 loader 调用封装成 loadComponent,同时监听定时器和加载函数返回的 Promise:
function loadComponent() {
return new Promise((resolve, reject) => {
if (timeout && timeout > 0) {
setTimeout(() => {
reject(new Error('Async component timed out'))
}, timeout)
}
loader().then(resolve, reject)
})
}
当 loader 加载成功时,Promise 会进入成功状态;当加载失败或超过指定时间时,Promise 会进入失败状态。
由于 Promise 的状态一旦确定就不会再次改变,即使超时和加载完成几乎同时发生,也只会处理第一次触发的结果。