异步组件 + 路由懒加载 综合 Demo
在 Vue 3 中,路由懒加载 本身就是基于异步组件实现的(() => import() 返回 Promise)。但如果你需要更精细的控制(如加载状态、错误处理、超时等),可以结合 defineAsyncComponent 在路由配置中或组件内部使用。
下面给出一个完整的 Demo,展示两种组合方式:
1. 路由配置中使用 defineAsyncComponent(带加载/错误状态)
javascript
// router/index.js
import { createRouter, createWebHistory } from 'vue-router'
import { defineAsyncComponent } from 'vue'
// 定义一个 Loading 组件(可以是任何 Vue 组件)
const LoadingComponent = {
template: `<div style="text-align:center; padding: 20px; color: #409eff;">⏳ 页面加载中...</div>`
}
// 定义一个错误组件
const ErrorComponent = {
template: `<div style="text-align:center; padding: 20px; color: red;">❌ 加载失败,请稍后重试</div>`
}
// 使用 defineAsyncComponent 包装路由组件
const Home = defineAsyncComponent({
loader: () => import('@/views/Home.vue'),
loadingComponent: LoadingComponent,
errorComponent: ErrorComponent,
delay: 200, // 延迟 200ms 显示 loading,避免闪烁
timeout: 3000 // 超时时间 3s
})
const About = defineAsyncComponent({
loader: () => import('@/views/About.vue'),
loadingComponent: LoadingComponent,
errorComponent: ErrorComponent,
delay: 200,
timeout: 3000
})
// 普通路由(不使用 defineAsyncComponent,直接用 import 函数也可,但无自定义状态)
const routes = [
{ path: '/', name: 'Home', component: Home },
{ path: '/about', name: 'About', component: About },
// 普通懒加载(不加额外配置)
{ path: '/dashboard', name: 'Dashboard', component: () => import('@/views/Dashboard.vue') }
]
const router = createRouter({
history: createWebHistory(),
routes
})
export default router
2. 在路由懒加载的组件内部,再使用异步子组件
Dashboard.vue 组件(被路由懒加载),其中又嵌套了一个异步图表组件。
vue
<!-- views/Dashboard.vue -->
<template>
<div>
<h2>仪表盘</h2>
<p>这是一个被路由懒加载的页面,内部又包含一个异步子组件:</p>
<!-- 异步子组件 -->
<AsyncChart />
</div>
</template>
<script setup>
import { defineAsyncComponent } from 'vue'
// 在组件内部定义异步子组件,同样可以配置 loading / error
const AsyncChart = defineAsyncComponent({
loader: () => import('@/components/Chart.vue'),
loadingComponent: {
template: `<div style="border:1px solid #ccc; padding: 20px;">📊 图表加载中...</div>`
},
delay: 300
})
</script>
3. 异步子组件示例(Chart.vue)
vue
<!-- components/Chart.vue -->
<template>
<div style="border: 1px solid #42b883; padding: 20px; border-radius: 8px;">
<h3>📈 实时数据图表</h3>
<p>这里是模拟的图表内容(数据:{{ data }})</p>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
const data = ref([])
onMounted(() => {
// 模拟异步数据请求
setTimeout(() => {
data.value = [10, 20, 30, 40, 50]
}, 500)
})
</script>
4. 父组件 App.vue(路由出口)
vue
<template>
<div id="app">
<nav>
<router-link to="/">首页</router-link> |
<router-link to="/about">关于</router-link> |
<router-link to="/dashboard">仪表盘</router-link>
</nav>
<router-view />
</div>
</template>
<style>
nav { margin-bottom: 20px; }
router-link { margin: 0 10px; }
</style>
运行效果说明
- 首次进入首页 :路由组件使用
defineAsyncComponent,会显示"⏳ 页面加载中...",加载完成后显示首页内容。 - 切换到"关于":同样显示加载状态(如果网络慢或配置了延迟)。
- 切换到"仪表盘" :这个路由是普通
() => import()形式,页面会直接显示(无 loading 状态),但内部嵌套的AsyncChart会显示"📊 图表加载中...",加载完成后显示图表。 - 如果加载超时(模拟慢网络),会显示错误组件(可自行测试)。
核心知识点总结
| 使用方式 | 优点 | 适用场景 |
|---|---|---|
路由配置中使用 defineAsyncComponent |
可配置全局统一的 loading/error 状态 | 页面级组件需要优雅的加载反馈 |
组件内部使用 defineAsyncComponent |
粒度更细,可针对某一块内容单独控制 | 复杂页面中的局部模块(如图表、编辑器等) |
直接用 () => import() |
简单,无需额外配置 | 对加载状态要求不高,追求极致简洁 |
注意点
defineAsyncComponent仅在 Vue 3 中可用 ,Vue 2 需要使用Vue.component或插件。- 路由懒加载的
() => import()本身返回的就是一个异步组件,Vue Router 内部会自动处理,所以直接使用也能正常工作。但加上defineAsyncComponent后可以获得更多控制。 - 开发环境 中,由于 Vite/Webpack 的热更新,加载很快,可能看不到 loading 效果,可以故意增加
delay值来观察。 - 如果多个路由共用相同的 loading 组件,可以抽取成公共变量(如上面示例中的
LoadingComponent)。