Vue3动态组件&异步组件用法及说明

 更新时间:2026年04月29日 10:37:43   作者:难渡-  
动态组件通过`<component:is="组件标识">`语法实现不同组件的动态渲染,支持组件对象、全局组件名称、异步组件等多种形式,结合`<KeepAlive>`可保留组件状态,配合异步组件实现按需加载,优化首屏性能,通过`defineAsyncComponent`支持异步加载,提供加载状态处理和配置项优化

动态组件

动态组件是通过 <component :is="组件标识"> 语法,让 Vue 动态渲染不同组件的特性 —— 你只需修改 :is 绑定的值,Vue 就会自动替换渲染的组件,无需写大量 v-if/v-else 分支。

核心特点

  • 一个<component>标签即可渲染任意组件;
  • 支持绑定「组件对象」「组件名称(全局注册)」「异步组件」;
  • 配合 <KeepAlive> 可保留组件状态(如输入框内容、滚动位置);
  • 可以根据不同的条件灵活切换显示不同的组件,核心是通过component标签和is属性;
  • 可以动态传递props/事件。

基础用法

1. 核心语法:绑定组件对象

Vue3 中 <script setup> 下组件不会自动注册为全局名称,直接绑定组件对象是最优写法:

<template>
  <div>
    <!-- 切换按钮 -->
    <button @click="currentComp = CompA">组件A</button>
    <button @click="currentComp = CompB">组件B</button>
    <button @click="currentComp = CompC">组件C</button>
    <!-- 动态组件核心标签 -->
    <component :is="currentComp"></component>
  </div>
</template>
<script setup>
import { ref } from 'vue'
// 导入需要切换的组件
import CompA from './CompA.vue'
import CompB from './CompB.vue'
import CompC from './CompC.vue'
// 绑定组件对象(响应式)
const currentComp = ref(CompA) // 默认渲染 CompA
</script>

2. 绑定全局组件名称

如果组件已全局注册(在 main.js 中注册),可绑定字符串名称:

// main.js 全局注册组件
import { createApp } from 'vue'
import App from './App.vue'
import CompA from './CompA.vue'

const app = createApp(App)
app.component('CompA', CompA) // 全局注册
app.mount('#app')
<template>
  <component :is="currentCompName"></component>
</template>

<script setup>
import { ref } from 'vue'
const currentCompName = ref('CompA') // 绑定全局组件名称
</script>

进阶技巧

1. 保留组件状态:结合

动态组件默认切换时会销毁旧组件、创建新组件(状态丢失),<KeepAlive> 可缓存组件实例,保留状态:

<template>
  <div>
    <button @click="currentComp = CompA">组件A</button>
    <button @click="currentComp = CompB">组件B</button>

    <!-- KeepAlive 缓存组件,切换后输入框内容不丢失 -->
    <KeepAlive>
      <component :is="currentComp"></component>
    </KeepAlive>
  </div>
</template>

<!-- CompA.vue 示例(带输入框) -->
<template>
    <input v-model="inputVal" placeholder="组件A输入内容" />
</template>
<script setup>
import { ref } from 'vue'
const inputVal = ref('') // 状态会被 KeepAlive 保留
</script>

KeepAlive 高级配置:

  • - include:仅缓存指定组件(支持字符串 / 正则 / 数组);
  • - exclude:排除不需要缓存的组件;
  • - max:限制缓存组件的最大数量(避免内存溢出)。

2. 动态传参:Props / 事件透传

动态组件支持像普通组件一样传递 Props 和监听事件:

<template>
  <component
    :is="currentComp"
    :msg="parentMsg"
    @change="handleCompChange"
  ></component>
</template>

<script setup>
import { ref } from 'vue'
import CompA from './CompA.vue'

const currentComp = ref(CompA)
const parentMsg = ref('父组件传递的消息')

const handleCompChange = (val) => {
  console.log('子组件触发的事件:', val)
}
</script>

3. 异步加载:结合 defineAsyncComponent

动态组件 + 异步组件 = 「按需加载 + 动态切换」,优化首屏性能:

<template>
  <component :is="currentComp"></component>
</template>

<script setup>
import { ref, defineAsyncComponent } from 'vue'

// 异步加载组件(切换时才加载代码)
const CompA = defineAsyncComponent(() => import('./CompA.vue'))
const CompB = defineAsyncComponent(() => import('./CompB.vue'))

const currentComp = ref(CompA)
</script>

带加载 / 错误状态的异步组件:

<script setup>
import { ref, defineAsyncComponent } from 'vue'
import Loading from './Loading.vue'
import Error from './Error.vue'

// 配置异步组件的加载/错误状态
const CompA = defineAsyncComponent({
  loader: () => import('./CompA.vue'),
  loadingComponent: Loading, // 加载中显示
  errorComponent: Error,     // 加载失败显示
  delay: 200,                // 延迟显示加载组件(避免闪屏)
  timeout: 3000              // 超时时间
})

const currentComp = ref(CompA)
</script>

4. 动态组件的生命周期

被 <KeepAlive> 缓存的动态组件,会触发两个特殊生命周期:

  • activated:组件被激活(切换显示)时触发;
  • deactivated:组件被失活(切换隐藏)时触发。
<!-- CompA.vue -->
<script setup>
import { onActivated, onDeactivated } from 'vue'

onActivated(() => {
  console.log('CompA 被激活(显示)')
})

onDeactivated(() => {
  console.log('CompA 被失活(隐藏)')
})
</script>

异步组件

异步组件是指组件在需要渲染时才会被加载(而非页面初始化时) 的组件,底层依赖 ES6 的 import() 动态导入语法和 Vue 提供的 defineAsyncComponent 封装函数。

核心价值

  • 拆分代码包体积,首屏只加载核心代码,非核心组件按需加载;
  • 降低首屏加载时间,提升页面响应速度;
  • 配合动态组件、路由懒加载,实现更灵活的组件加载策略。

基础用法

1.最简写法

通过 defineAsyncComponent 包裹 import() 函数,返回异步组件对象:

<template>
  <!-- 像普通组件一样使用异步组件 -->
  <AsyncComp />
</template>

<script setup>
import { defineAsyncComponent } from 'vue'

// 定义异步组件(最简写法)
const AsyncComp = defineAsyncComponent(() => {
  // import() 动态导入组件,返回 Promise
  return import('./AsyncComp.vue')
})
</script>

2.核心逻辑解析

  • import('./AsyncComp.vue'):ES6 动态导入,返回一个 Promise,只有当组件需要渲染时才会执行这个导入操作;
  • defineAsyncComponent:Vue 封装的异步组件处理函数,接收加载函数 / 配置对象,返回一个 “包装器组件”(同步组件),Vue 会先渲染包装器,等真实组件加载完成后再替换。

进阶配置

实际开发中,需要处理「加载中、加载失败、超时、延迟」等场景,defineAsyncComponent 支持完整的配置项:

<template>
  <AsyncComp />
</template>

<script setup>
import { defineAsyncComponent } from 'vue'
// 导入加载/错误兜底组件
import LoadingComp from './LoadingComp.vue'
import ErrorComp from './ErrorComp.vue'

// 完整配置的异步组件
const AsyncComp = defineAsyncComponent({
  // 1. 核心:加载函数(返回 Promise)
  loader: () => import('./AsyncComp.vue'),

  // 2. 加载中显示的组件(可选)
  loadingComponent: LoadingComp,
  // 延迟显示加载组件(避免闪屏,默认 200ms)
  delay: 200,

  // 3. 加载失败显示的组件(可选)
  errorComponent: ErrorComp,
  // 加载失败的重试逻辑(可选)
  onError: (err, retry, fail, attempts) => {
    // err:错误信息
    // retry:重试加载的函数
    // fail:终止加载的函数
    // attempts:已重试次数
    if (attempts < 3) {
      // 重试 3 次
      setTimeout(retry, 1000)
    } else {
      // 超过 3 次则失败
      fail()
    }
  },

  // 4. 超时时间(可选,毫秒)
  timeout: 5000, // 5 秒加载不完成则触发失败
})
</script>

配置项说明

配置项类型作用默认值
loaderFunction动态导入组件的函数(必须返回 Promise)-
loadingComponentComponent加载中显示的兜底组件undefined
delayNumber延迟显示加载组件的时间(避免闪屏)200
errorComponentComponent加载失败显示的兜底组件undefined
onErrorFunction加载失败的回调(支持重试)undefined
timeoutNumber加载超时时间(超时触发失败)Infinity

常见使用场景

1.结合动态组件使用(按需切换加载)

异步组件 + 动态组件 = 「切换时才加载组件代码」,适合 Tab 标签页等场景:

<template>
  <button @click="currentComp = AsyncTab1">标签1</button>
  <button @click="currentComp = AsyncTab2">标签2</button>
  
  <component :is="currentComp"></component>
</template>

<script setup>
import { ref, defineAsyncComponent } from 'vue'

// 定义多个异步组件
const AsyncTab1 = defineAsyncComponent(() => import('./Tab1.vue'))
const AsyncTab2 = defineAsyncComponent(() => import('./Tab2.vue'))

const currentComp = ref(AsyncTab1)
</script>

2.路由懒加载(最常用场景)

Vue Router 中结合异步组件实现路由级别的按需加载,是项目优化的核心手段:

// router/index.js
import { createRouter, createWebHistory } from 'vue-router'
import { defineAsyncComponent } from 'vue'
import Loading from '@/components/Loading.vue'

// 路由懒加载(方式1:最简)
const Home = () => import('@/views/Home.vue')

// 路由懒加载(方式2:带配置)
const About = defineAsyncComponent({
  loader: () => import('@/views/About.vue'),
  loadingComponent: Loading,
  delay: 100
})

const router = createRouter({
  history: createWebHistory(),
  routes: [
    { path: '/', component: Home },
    { path: '/about', component: About }
  ]
})

export default router

3. 条件渲染的异步组件

仅当满足条件时才加载组件,避免无用代码加载:

<template>
  <button @click="showModal = true">打开弹窗</button>
  <!-- 弹窗显示时才加载组件 -->
  <AsyncModal v-if="showModal" @close="showModal = false" />
</template>

<script setup>
import { ref, defineAsyncComponent } from 'vue'

const showModal = ref(false)
// 弹窗组件仅在需要时加载
const AsyncModal = defineAsyncComponent(() => import('./Modal.vue'))
</script>

原理补充

defineAsyncComponent 的核心逻辑是返回一个「包装器组件」:

  • 1.包装器组件先渲染 loadingComponent(延迟后);
  • 2.执行 loader 函数加载真实组件;
  • 3.加载成功:替换为真实组件;
  • 4.加载失败 / 超时:渲染 errorComponent

简化版伪代码:

function defineAsyncComponent(options) {
  return {
    setup() {
      const state = ref('loading') // loading/success/error
      const component = ref(null)
      
      // 执行加载
      options.loader()
        .then(comp => {
          component.value = comp.default
          state.value = 'success'
        })
        .catch(() => state.value = 'error')
      
      return { state, component }
    },
    render() {
      if (this.state === 'loading') return h(options.loadingComponent)
      if (this.state === 'error') return h(options.errorComponent)
      return h(this.component)
    }
  }
}

总结

1.核心语法defineAsyncComponent 包裹 import() 是异步组件的基础,支持最简写法和完整配置;

2.核心配置:重点掌握 loadingComponent/errorComponent/delay/timeout,处理加载状态;

3.核心场景:路由懒加载、动态组件切换、条件渲染的非核心组件;

4.优化原则:按需拆分、做好兜底、避免过度拆分。

5.简单记:异步组件 = 按需加载 + 状态兜底 + 体积优化,是 Vue 项目性能优化的 “标配” 手段。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • vue3如何解决slot深层透传问题

    vue3如何解决slot深层透传问题

    这篇文章主要介绍了vue3如何解决slot深层透传问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2025-04-04
  • vue-cli入门之项目结构分析

    vue-cli入门之项目结构分析

    本篇文章主要介绍了vue-cli入门之项目结构,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-04-04
  • Vue 滚动行为的具体使用方法

    Vue 滚动行为的具体使用方法

    本篇文章主要介绍了Vue 滚动行为的具体使用方法。小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-09-09
  • Vue实现天气预报小应用

    Vue实现天气预报小应用

    这篇文章主要为大家详细介绍了Vue实现天气预报小应用,查询一些城市的天气情况,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-08-08
  • node.js开发辅助工具nodemon安装与配置详解

    node.js开发辅助工具nodemon安装与配置详解

    node.js代码修改后,需要重新启动 Express 应用,所做的修改才能生效。若之后的每次代码修改都要重复这样的操作,势必会影响开发效率,本文将详细介绍Nodemon,它会监测项目中的所有文件,一旦发现文件有改动,Nodemon 会自动重启应用
    2020-02-02
  • WebSocket用法与在 Vue 中的使用指南

    WebSocket用法与在 Vue 中的使用指南

    文章介绍了WebSocket协议的基础概念、特点及与HTTP的区别,详解了在Vue中的集成方式(组件内/插件封装/Vuex管理),并涵盖连接管理、安全性、数据格式等最佳实践,列举了Socket.IO和SockJS等常用库,适用于实时通信场景,感兴趣的朋友一起看看吧
    2025-08-08
  • vuex模块获取数据及方法的简单示例

    vuex模块获取数据及方法的简单示例

    Vuex是一个专为Vue.js应用程序开发的状态管理模式,它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化,下面这篇文章主要给大家介绍了关于vuex模块获取数据及方法的相关资料,需要的朋友可以参考下
    2023-03-03
  • element-plus的自动导入和按需导入方式详解

    element-plus的自动导入和按需导入方式详解

    之前使用 ElementPlus 做项目的时候,由于不会使用按需引入耽误了不少时间,这篇文章主要给大家介绍了关于element-plus自动导入和按需导入的相关资料,需要的朋友可以参考下
    2022-08-08
  • vue中关于@media媒体查询的使用

    vue中关于@media媒体查询的使用

    这篇文章主要介绍了vue中关于@media媒体查询的使用,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-08-08
  • Vue-cli Eslint在vscode里代码自动格式化的方法

    Vue-cli Eslint在vscode里代码自动格式化的方法

    本篇文章主要介绍了Vue-cli Eslint在vscode里代码自动格式化的方法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-02-02

最新评论