Vue 3 中 defineAsyncComponent 异步组件
FreeGuideOnline
最新
2026-07-07
**注意:** 与 Vue 2 不同,Vue 3 中不再需要在组件注册时直接将异步函数赋值给组件选项,而是统一使用 `defineAsyncComponent`。
---
## 高级选项:控制加载与错误状态
`defineAsyncComponent` 还支持一个选项对象,用于更精细地控制加载行为、错误处理和超时。
```javascript
const AsyncComp = defineAsyncComponent({
// 加载工厂函数
loader: () => import('./HeavyComponent.vue'),
// 加载中时显示的组件
loadingComponent: {
template: '<div class="loading-spinner">加载中...</div>'
},
// 加载失败时显示的组件
errorComponent: {
template: '<div class="error-box">组件加载失败,请刷新重试</div>'
},
// 显示加载组件前的延迟时间(毫秒)。如果加载快于该时间,则不会显示 loading
delay: 200,
// 超时时间(毫秒)。超时后将显示错误组件
timeout: 3000,
// 定义组件是否可挂起(与 Suspense 相关),默认为 true
suspensible: true,
// 错误处理回调:可在此重试或记录日志
onError(error, retry, fail, attempts) {
// retry 是一个函数,调用后会重新尝试加载
// fail 用于放弃加载,显示错误组件
if (error.message.includes('fetch') && attempts <= 3) {
retry()
} else {
fail()
}
}
})
配置项详解
| 选项 | 类型 | 说明 |
|---|---|---|
loader |
() => Promise<Component> |
返回组件定义的工厂函数 |
loadingComponent |
Component |
异步加载过程中显示的占位组件 |
errorComponent |
Component |
加载失败后展示的回退组件 |
delay |
number |
延迟展示 loadingComponent 的毫秒数,避免闪烁 |
timeout |
number |
超时时间,超时后直接显示 errorComponent |
suspensible |
boolean |
是否支持与 <Suspense> 集成 |
onError |
(error, retry, fail, attempts) => void |
错误处理钩子,可实现重试逻辑 |
实战示例:带加载指示的异步表格组件
假设我们要创建一个数据量较大的表格组件,希望它只在用户访问该页面时才加载。
1. 异步表格组件 DataTable.vue
<template>
<table>
<thead>
<tr><th>ID</th><th>名称</th></tr>
</thead>
<tbody>
<tr v-for="item in data" :key="item.id">
<td>{{ item.id }}</td>
<td>{{ item.name }}</td>
</tr>
</tbody>
</table>
</template>
<script setup>
defineProps({
data: Array
})
</script>
2. 定义异步组件并处理加载状态
<template>
<div>
<h2>用户列表</h2>
<AsyncTable :data="userList" />
</div>
</template>
<script setup>
import { ref, defineAsyncComponent } from 'vue'
const userList = ref([
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' }
])
const AsyncTable = defineAsyncComponent({
loader: () => import('./DataTable.vue'),
loadingComponent: {
template: '<p class="text-gray">表格加载中...</p>'
},
errorComponent: {
template: '<p class="text-red">加载失败,请稍后重试</p>'
},
delay: 300,
timeout: 5000
})
</script>
与 <Suspense> 配合使用
当异步组件内部存在异步依赖(如 async setup() 或 top-level await)时,<Suspense> 可以优雅地协调多个异步操作的加载状态。
<template>
<Suspense>
<!-- 默认插槽:异步组件 -->
<AsyncDashboard />
<!-- fallback 插槽:加载中占位 -->
<template #fallback>
<div class="spinner">仪表板加载中...</div>
</template>
</Suspense>
</template>
<script setup>
import { defineAsyncComponent } from 'vue'
const AsyncDashboard = defineAsyncComponent(() =>
import('./Dashboard.vue')
)
</script>
关键点:
AsyncDashboard内如果包含await的setup,<Suspense>会捕获其状态并显示 fallback 内容。- 若设置了
suspensible: false,该组件将不受外部<Suspense>控制,转而使用自身的 loading/error 逻辑。
常见问题与最佳实践
1. 如何实现加载失败后的重试?
在 onError 钩子中利用 retry 函数,可以方便地实现有限次数的重试。
onError(error, retry, fail, attempts) {
if (attempts < 2) {
console.log(`第 ${attempts + 1} 次重试...`)
retry()
} else {
fail()
}
}
2. 异步组件可以用在 v-for 内部吗?
可以,但每个组件实例都会独立加载,可能触发大量网络请求。如果列表项相同,建议在父级使用同一个异步组件引用。
3. 如何预加载异步组件?
在用户即将访问时(如鼠标悬停)提前加载。
const loadComponent = () => import('./MyComponent.vue')
// 预加载
onMounted(() => {
loadComponent()
})
// 定义异步组件
const AsyncComp = defineAsyncComponent(() => loadComponent())