查看: 188|回复: 1

Vue3动态组件与异步组件实战:按需加载与状态保留详解

[复制链接]
发表于 昨天 21:00 | 显示全部楼层 |阅读模式
在 Vue3 项目开发中,动态组件和异步组件是提升代码灵活性与性能的常用技术。动态组件通过 <component :is="..."> 实现不同组件的切换,避免大量 v-if/v-else;异步组件则利用 import() 实现按需加载,减少首屏体积。本文结合实际代码和配置参数,详细拆解这两类组件的正确用法与最佳实践。

一、动态组件核心用法

1. 绑定组件对象(推荐)
在 <script setup> 中,组件不会自动注册为全局名称,直接绑定导入的组件对象是最稳写法:
  1. <template>
  2.   <div>
  3.     <button @click="currentComp = CompA">组件A</button>
  4.     <button @click="currentComp = CompB">组件B</button>
  5.     <button @click="currentComp = CompC">组件C</button>
  6.     <component :is="currentComp"></component>
  7.   </div>
  8. </template>
  9. <script setup>
  10. import { ref } from 'vue'
  11. import CompA from './CompA.vue'
  12. import CompB from './CompB.vue'
  13. import CompC from './CompC.vue'
  14. const currentComp = ref(CompA)
  15. </script>
复制代码

2. 绑定全局组件名称
若在 main.js 中通过 app.component() 全局注册过组件,可绑定字符串名称:
  1. // main.js
  2. import { createApp } from 'vue'
  3. import App from './App.vue'
  4. import CompA from './CompA.vue'
  5. const app = createApp(App)
  6. app.component('CompA', CompA)
  7. app.mount('#app')
  8. // 组件内
  9. <template>
  10.   <component :is="currentCompName"></component>
  11. </template>
  12. <script setup>
  13. import { ref } from 'vue'
  14. const currentCompName = ref('CompA')
  15. </script>
复制代码

二、进阶技巧:状态保留与参数传递

1. 配合 <KeepAlive> 保留组件状态
默认切换时,旧组件会被销毁、新组件重建(输入框内容、滚动位置等状态丢失)。用 <KeepAlive> 包裹 <component> 后,组件实例会被缓存,切换回来时状态保留:
  1. <template>
  2.   <div>
  3.     <button @click="currentComp = CompA">组件A</button>
  4.     <button @click="currentComp = CompB">组件B</button>
  5.     <KeepAlive>
  6.       <component :is="currentComp"></component>
  7.     </KeepAlive>
  8.   </div>
  9. </template>
  10. <!-- CompA.vue -->
  11. <template>
  12.   <input v-model="inputVal" placeholder="组件A输入内容" />
  13. </template>
  14. <script setup>
  15. import { ref } from 'vue'
  16. const inputVal = ref('')
  17. </script>
复制代码

KeepAlive 还支持 include(包含)、exclude(排除)和 max(最大缓存数量)等高级配置。被缓存的组件会触发 onActivated 和 onDeactivated 两个特殊生命周期:
  1. <script setup>
  2. import { onActivated, onDeactivated } from 'vue'
  3. onActivated(() => { console.log('组件被激活') })
  4. onDeactivated(() => { console.log('组件被失活') })
  5. </script>
复制代码

2. 动态传参与事件监听
动态组件可以像普通组件一样传递 props 和绑定事件,只需在 <component> 上直接写属性和 @ 事件:
  1. <template>
  2.   <component :is="currentComp" :msg="parentMsg" @change="handleChange"></component>
  3. </template>
  4. <script setup>
  5. import { ref } from 'vue'
  6. import CompA from './CompA.vue'
  7. const currentComp = ref(CompA)
  8. const parentMsg = ref('从父组件传来的消息')
  9. const handleChange = (val) => { console.log('子组件事件:', val) }
  10. </script>
复制代码

三、异步组件:按需加载与配置

异步组件通过 defineAsyncComponent 包裹 import() 实现,组件在需要渲染时才发起网络请求加载代码,核心价值是拆分代码包、降低首屏加载时间。

1. 最简用法
  1. <template>
  2.   <AsyncComp />
  3. </template>
  4. <script setup>
  5. import { defineAsyncComponent } from 'vue'
  6. const AsyncComp = defineAsyncComponent(() => import('./AsyncComp.vue'))
  7. </script>
复制代码

defineAsyncComponent 接收一个返回 Promise 的加载函数,Vue 会自动处理组件的挂载与替换。

2. 完整配置(加载/错误/超时/重试)
实际开发中需要处理加载中、加载失败、超时等场景:
  1. <template>
  2.   <AsyncComp />
  3. </template>
  4. <script setup>
  5. import { defineAsyncComponent } from 'vue'
  6. import LoadingComp from './LoadingComp.vue'
  7. import ErrorComp from './ErrorComp.vue'
  8. const AsyncComp = defineAsyncComponent({
  9.   loader: () => import('./AsyncComp.vue'),
  10.   loadingComponent: LoadingComp,   // 加载中显示的组件
  11.   delay: 200,                      // 延迟显示加载组件(防闪屏)
  12.   errorComponent: ErrorComp,       // 加载失败显示的组件
  13.   timeout: 5000,                   // 超时时间(毫秒)
  14.   onError: (err, retry, fail, attempts) => {
  15.     if (attempts < 3) {
  16.       setTimeout(retry, 1000)      // 重试3次
  17.     } else {
  18.       fail()                       // 放弃加载
  19.     }
  20.   }
  21. })
  22. </script>
复制代码

参数说明:loader 是必需的加载函数;loadingComponent 和 delay 控制加载中状态;errorComponent 和 onError 处理失败;timeout 设置超时。

四、常见使用场景

1. 动态组件 + 异步组件(Tab切换按需加载)
切换 Tab 时才加载对应组件代码,非常适合标签页或步骤条场景:
  1. <template>
  2.   <button @click="currentComp = AsyncTab1">标签1</button>
  3.   <button @click="currentComp = AsyncTab2">标签2</button>
  4.   <component :is="currentComp"></component>
  5. </template>
  6. <script setup>
  7. import { ref, defineAsyncComponent } from 'vue'
  8. const AsyncTab1 = defineAsyncComponent(() => import('./Tab1.vue'))
  9. const AsyncTab2 = defineAsyncComponent(() => import('./Tab2.vue'))
  10. const currentComp = ref(AsyncTab1)
  11. </script>
复制代码

2. 路由懒加载
在 Vue Router 中结合异步组件实现按需加载,是项目性能优化的标配:
  1. // router/index.js
  2. import { createRouter, createWebHistory } from 'vue-router'
  3. const Home = () => import('@/views/Home.vue')
  4. const About = {
  5.   loader: () => import('@/views/About.vue'),
  6.   loadingComponent: () => import('@/components/Loading.vue')
  7. }
  8. const routes = [
  9.   { path: '/', component: Home },
  10.   { path: '/about', component: About }
  11. ]
  12. export default createRouter({ history: createWebHistory(), routes })
复制代码

3. 条件渲染的非核心组件
比如弹窗、抽屉等组件,仅在触发显示时才加载代码:
  1. <template>
  2.   <button @click="showModal = true">打开弹窗</button>
  3.   <AsyncModal v-if="showModal" @close="showModal = false" />
  4. </template>
  5. <script setup>
  6. import { ref, defineAsyncComponent } from 'vue'
  7. const showModal = ref(false)
  8. const AsyncModal = defineAsyncComponent(() => import('./Modal.vue'))
  9. </script>
复制代码

五、原理补充
defineAsyncComponent 返回一个“包装器组件”,内部逻辑简化为:
- 先渲染 loadingComponent(带 delay 延迟);
- 执行 loader(import())加载真实组件;
- 成功则替换为真实组件,失败则渲染 errorComponent;
- 支持超时和重试。

六、总结
动态组件和异步组件是 Vue3 项目开发中不可或缺的“灵活+性能”利器。核心记住:
- 动态组件用 <component :is="组件对象" /> 结合 KeepAlive 保留状态;
- 异步组件用 defineAsyncComponent + import() 按需加载,配合 loadingComponent、errorComponent、delay、timeout 做好兜底;
- 优先应用于路由懒加载、标签页切换、条件渲染等场景。

按需拆分、做好异常处理、避免过度拆分,即可有效优化首屏体积与用户体验。
回复

使用道具 举报

发表于 昨天 21:10 | 显示全部楼层

Re: Vue3动态组件与异步组件实战:按需加载与状态保留详解

看完了,干货满满!动态组件绑定对象的方式确实比字符串更直观,配合 KeepAlive 保留状态也是实际开发中经常会用到的技巧。想请教一下楼主,异步组件在使用时,怎么处理加载中的 loading 状态或者加载失败时的 fallback?有没有推荐的优雅写法?
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

指导单位

江苏省公安厅

江苏省通信管理局

浙江省台州刑侦支队

DEFCON GROUP 86025

Hacking Group 021A

旗下站点

态势感知中心

应急响应中心

红盟安全

联系我们

官方QQ群:112851260

官方邮箱:security#ihonker.org(#改成@)

官方核心成员

关注微信公众号

Archiver|手机版|小黑屋| ( 沪ICP备2021026908号 )

GMT+8, 2026-6-12 01:45 , Processed in 0.032066 second(s), 17 queries , Gzip On, Redis On.

Powered by ihonker.com

Copyright © 2015-现在.

  • 返回顶部