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 项目性能优化的 “标配” 手段。

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

相关文章

  • vue中let that=this的作用及说明

    vue中let that=this的作用及说明

    这篇文章主要介绍了vue中let that=this的作用及说明,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-10-10
  • vue中英文切换实例代码

    vue中英文切换实例代码

    在本篇文章里小编给大家整理了关于vue中英文切换实例代码,需要的朋友们学习参考下。
    2020-01-01
  • Vue中引入json的三种方式总结

    Vue中引入json的三种方式总结

    这篇文章主要介绍了Vue中引入json的三种方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-05-05
  • vue如何设置定时器和清理定时器

    vue如何设置定时器和清理定时器

    这篇文章主要介绍了vue如何设置定时器和清理定时器,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-05-05
  • 详解VUE项目中安装和使用vant组件

    详解VUE项目中安装和使用vant组件

    这篇文章主要介绍了VUE安装和使用vant组件,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-04-04
  • Vue输入框状态切换&自动获取输入框焦点的实现方法

    Vue输入框状态切换&自动获取输入框焦点的实现方法

    这篇文章主要介绍了Vue输入框状态切换&自动获取输入框焦点的实现,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-05-05
  • Element Plus 菜单组件区别和用法最佳实践

    Element Plus 菜单组件区别和用法最佳实践

    本文介绍了ElementPlus中的菜单组件,包括el-menu、el-menu-item、el-sub-menu和el-menu-item-group的特性、用法以及最佳实践,通过对比不同场景的使用指南,帮助开发者根据需求选择合适的组件组合,构建功能强大且用户友好的菜单系统,感兴趣的朋友跟随小编一起看看吧
    2026-01-01
  • vue使用Print.js打印页面样式不出现的解决

    vue使用Print.js打印页面样式不出现的解决

    这篇文章主要介绍了vue使用Print.js打印页面样式不出现的解决,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-10-10
  • 写给vue新手们的vue渲染页面教程

    写给vue新手们的vue渲染页面教程

    这篇文章主要给大家分享了一个写给vue新手看的vue渲染页面教程,文中通过图文介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,感兴趣的朋友们下面随着小编来一起学习学习吧。
    2017-09-09
  • Vite 打包目录结构自定义配置小结

    Vite 打包目录结构自定义配置小结

    在Vite工程开发中,默认打包后的dist目录资源常集中在asset目录下,不利于资源管理,本文基于Rollup配置原理,本文就来介绍一下通过Vite配置自定义打包目录结构,感兴趣的可以了解一下
    2025-08-08

最新评论