异步组件 + 路由懒加载 综合 Demo

异步组件 + 路由懒加载 综合 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() 简单,无需额外配置 对加载状态要求不高,追求极致简洁

注意点

  1. defineAsyncComponent 仅在 Vue 3 中可用 ,Vue 2 需要使用 Vue.component 或插件。
  2. 路由懒加载的 () => import() 本身返回的就是一个异步组件,Vue Router 内部会自动处理,所以直接使用也能正常工作。但加上 defineAsyncComponent 后可以获得更多控制。
  3. 开发环境 中,由于 Vite/Webpack 的热更新,加载很快,可能看不到 loading 效果,可以故意增加 delay 值来观察。
  4. 如果多个路由共用相同的 loading 组件,可以抽取成公共变量(如上面示例中的 LoadingComponent)。