Vue3動態(tài)組件&異步組件用法及說明
動態(tài)組件
動態(tài)組件是通過 <component :is="組件標(biāo)識"> 語法,讓 Vue 動態(tài)渲染不同組件的特性 —— 你只需修改 :is 綁定的值,Vue 就會自動替換渲染的組件,無需寫大量 v-if/v-else 分支。
核心特點(diǎn)
- 一個
<component>標(biāo)簽即可渲染任意組件; - 支持綁定「組件對象」「組件名稱(全局注冊)」「異步組件」;
- 配合
<KeepAlive>可保留組件狀態(tài)(如輸入框內(nèi)容、滾動位置); - 可以根據(jù)不同的條件靈活切換顯示不同的組件,核心是通過component標(biāo)簽和is屬性;
- 可以動態(tài)傳遞props/事件。
基礎(chǔ)用法
1. 核心語法:綁定組件對象
Vue3 中 <script setup> 下組件不會自動注冊為全局名稱,直接綁定組件對象是最優(yōu)寫法:
<template>
<div>
<!-- 切換按鈕 -->
<button @click="currentComp = CompA">組件A</button>
<button @click="currentComp = CompB">組件B</button>
<button @click="currentComp = CompC">組件C</button>
<!-- 動態(tài)組件核心標(biāo)簽 -->
<component :is="currentComp"></component>
</div>
</template>
<script setup>
import { ref } from 'vue'
// 導(dǎo)入需要切換的組件
import CompA from './CompA.vue'
import CompB from './CompB.vue'
import CompC from './CompC.vue'
// 綁定組件對象(響應(yīng)式)
const currentComp = ref(CompA) // 默認(rèn)渲染 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>
進(jìn)階技巧
1. 保留組件狀態(tài):結(jié)合
動態(tài)組件默認(rèn)切換時(shí)會銷毀舊組件、創(chuàng)建新組件(狀態(tài)丟失),<KeepAlive> 可緩存組件實(shí)例,保留狀態(tài):
<template>
<div>
<button @click="currentComp = CompA">組件A</button>
<button @click="currentComp = CompB">組件B</button>
<!-- KeepAlive 緩存組件,切換后輸入框內(nèi)容不丟失 -->
<KeepAlive>
<component :is="currentComp"></component>
</KeepAlive>
</div>
</template>
<!-- CompA.vue 示例(帶輸入框) -->
<template>
<input v-model="inputVal" placeholder="組件A輸入內(nèi)容" />
</template>
<script setup>
import { ref } from 'vue'
const inputVal = ref('') // 狀態(tài)會被 KeepAlive 保留
</script>
KeepAlive 高級配置:
- -
include:僅緩存指定組件(支持字符串 / 正則 / 數(shù)組); - -
exclude:排除不需要緩存的組件; - -
max:限制緩存組件的最大數(shù)量(避免內(nèi)存溢出)。
2. 動態(tài)傳參:Props / 事件透傳
動態(tài)組件支持像普通組件一樣傳遞 Props 和監(jiān)聽事件:
<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('子組件觸發(fā)的事件:', val)
}
</script>
3. 異步加載:結(jié)合 defineAsyncComponent
動態(tài)組件 + 異步組件 = 「按需加載 + 動態(tài)切換」,優(yōu)化首屏性能:
<template>
<component :is="currentComp"></component>
</template>
<script setup>
import { ref, defineAsyncComponent } from 'vue'
// 異步加載組件(切換時(shí)才加載代碼)
const CompA = defineAsyncComponent(() => import('./CompA.vue'))
const CompB = defineAsyncComponent(() => import('./CompB.vue'))
const currentComp = ref(CompA)
</script>
帶加載 / 錯誤狀態(tài)的異步組件:
<script setup>
import { ref, defineAsyncComponent } from 'vue'
import Loading from './Loading.vue'
import Error from './Error.vue'
// 配置異步組件的加載/錯誤狀態(tài)
const CompA = defineAsyncComponent({
loader: () => import('./CompA.vue'),
loadingComponent: Loading, // 加載中顯示
errorComponent: Error, // 加載失敗顯示
delay: 200, // 延遲顯示加載組件(避免閃屏)
timeout: 3000 // 超時(shí)時(shí)間
})
const currentComp = ref(CompA)
</script>
4. 動態(tài)組件的生命周期
被 <KeepAlive> 緩存的動態(tài)組件,會觸發(fā)兩個特殊生命周期:
activated:組件被激活(切換顯示)時(shí)觸發(fā);deactivated:組件被失活(切換隱藏)時(shí)觸發(fā)。
<!-- CompA.vue -->
<script setup>
import { onActivated, onDeactivated } from 'vue'
onActivated(() => {
console.log('CompA 被激活(顯示)')
})
onDeactivated(() => {
console.log('CompA 被失活(隱藏)')
})
</script>
異步組件
異步組件是指組件在需要渲染時(shí)才會被加載(而非頁面初始化時(shí)) 的組件,底層依賴 ES6 的 import() 動態(tài)導(dǎo)入語法和 Vue 提供的 defineAsyncComponent 封裝函數(shù)。
核心價(jià)值
- 拆分代碼包體積,首屏只加載核心代碼,非核心組件按需加載;
- 降低首屏加載時(shí)間,提升頁面響應(yīng)速度;
- 配合動態(tài)組件、路由懶加載,實(shí)現(xiàn)更靈活的組件加載策略。
基礎(chǔ)用法
1.最簡寫法
通過 defineAsyncComponent 包裹 import() 函數(shù),返回異步組件對象:
<template>
<!-- 像普通組件一樣使用異步組件 -->
<AsyncComp />
</template>
<script setup>
import { defineAsyncComponent } from 'vue'
// 定義異步組件(最簡寫法)
const AsyncComp = defineAsyncComponent(() => {
// import() 動態(tài)導(dǎo)入組件,返回 Promise
return import('./AsyncComp.vue')
})
</script>
2.核心邏輯解析
import('./AsyncComp.vue'):ES6 動態(tài)導(dǎo)入,返回一個 Promise,只有當(dāng)組件需要渲染時(shí)才會執(zhí)行這個導(dǎo)入操作;defineAsyncComponent:Vue 封裝的異步組件處理函數(shù),接收加載函數(shù) / 配置對象,返回一個 “包裝器組件”(同步組件),Vue 會先渲染包裝器,等真實(shí)組件加載完成后再替換。
進(jìn)階配置
實(shí)際開發(fā)中,需要處理「加載中、加載失敗、超時(shí)、延遲」等場景,defineAsyncComponent 支持完整的配置項(xiàng):
<template>
<AsyncComp />
</template>
<script setup>
import { defineAsyncComponent } from 'vue'
// 導(dǎo)入加載/錯誤兜底組件
import LoadingComp from './LoadingComp.vue'
import ErrorComp from './ErrorComp.vue'
// 完整配置的異步組件
const AsyncComp = defineAsyncComponent({
// 1. 核心:加載函數(shù)(返回 Promise)
loader: () => import('./AsyncComp.vue'),
// 2. 加載中顯示的組件(可選)
loadingComponent: LoadingComp,
// 延遲顯示加載組件(避免閃屏,默認(rèn) 200ms)
delay: 200,
// 3. 加載失敗顯示的組件(可選)
errorComponent: ErrorComp,
// 加載失敗的重試邏輯(可選)
onError: (err, retry, fail, attempts) => {
// err:錯誤信息
// retry:重試加載的函數(shù)
// fail:終止加載的函數(shù)
// attempts:已重試次數(shù)
if (attempts < 3) {
// 重試 3 次
setTimeout(retry, 1000)
} else {
// 超過 3 次則失敗
fail()
}
},
// 4. 超時(shí)時(shí)間(可選,毫秒)
timeout: 5000, // 5 秒加載不完成則觸發(fā)失敗
})
</script>
配置項(xiàng)說明
| 配置項(xiàng) | 類型 | 作用 | 默認(rèn)值 |
|---|---|---|---|
| loader | Function | 動態(tài)導(dǎo)入組件的函數(shù)(必須返回 Promise) | - |
| loadingComponent | Component | 加載中顯示的兜底組件 | undefined |
| delay | Number | 延遲顯示加載組件的時(shí)間(避免閃屏) | 200 |
| errorComponent | Component | 加載失敗顯示的兜底組件 | undefined |
| onError | Function | 加載失敗的回調(diào)(支持重試) | undefined |
| timeout | Number | 加載超時(shí)時(shí)間(超時(shí)觸發(fā)失敗) | Infinity |
常見使用場景
1.結(jié)合動態(tài)組件使用(按需切換加載)
異步組件 + 動態(tài)組件 = 「切換時(shí)才加載組件代碼」,適合 Tab 標(biāo)簽頁等場景:
<template>
<button @click="currentComp = AsyncTab1">標(biāo)簽1</button>
<button @click="currentComp = AsyncTab2">標(biāo)簽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 中結(jié)合異步組件實(shí)現(xiàn)路由級別的按需加載,是項(xiàng)目優(yōu)化的核心手段:
// 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. 條件渲染的異步組件
僅當(dāng)滿足條件時(shí)才加載組件,避免無用代碼加載:
<template>
<button @click="showModal = true">打開彈窗</button>
<!-- 彈窗顯示時(shí)才加載組件 -->
<AsyncModal v-if="showModal" @close="showModal = false" />
</template>
<script setup>
import { ref, defineAsyncComponent } from 'vue'
const showModal = ref(false)
// 彈窗組件僅在需要時(shí)加載
const AsyncModal = defineAsyncComponent(() => import('./Modal.vue'))
</script>
原理補(bǔ)充
defineAsyncComponent 的核心邏輯是返回一個「包裝器組件」:
- 1.包裝器組件先渲染
loadingComponent(延遲后); - 2.執(zhí)行 loader 函數(shù)加載真實(shí)組件;
- 3.加載成功:替換為真實(shí)組件;
- 4.加載失敗 / 超時(shí):渲染
errorComponent。
簡化版?zhèn)未a:
function defineAsyncComponent(options) {
return {
setup() {
const state = ref('loading') // loading/success/error
const component = ref(null)
// 執(zhí)行加載
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)
}
}
}
總結(jié)
1.核心語法:defineAsyncComponent 包裹 import() 是異步組件的基礎(chǔ),支持最簡寫法和完整配置;
2.核心配置:重點(diǎn)掌握 loadingComponent/errorComponent/delay/timeout,處理加載狀態(tài);
3.核心場景:路由懶加載、動態(tài)組件切換、條件渲染的非核心組件;
4.優(yōu)化原則:按需拆分、做好兜底、避免過度拆分。
5.簡單記:異步組件 = 按需加載 + 狀態(tài)兜底 + 體積優(yōu)化,是 Vue 項(xiàng)目性能優(yōu)化的 “標(biāo)配” 手段。

以上為個人經(jīng)驗(yàn),希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
vue如何設(shè)置定時(shí)器和清理定時(shí)器
這篇文章主要介紹了vue如何設(shè)置定時(shí)器和清理定時(shí)器,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-05-05
Vue輸入框狀態(tài)切換&自動獲取輸入框焦點(diǎn)的實(shí)現(xiàn)方法
這篇文章主要介紹了Vue輸入框狀態(tài)切換&自動獲取輸入框焦點(diǎn)的實(shí)現(xiàn),本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-05-05
Element Plus 菜單組件區(qū)別和用法最佳實(shí)踐
本文介紹了ElementPlus中的菜單組件,包括el-menu、el-menu-item、el-sub-menu和el-menu-item-group的特性、用法以及最佳實(shí)踐,通過對比不同場景的使用指南,幫助開發(fā)者根據(jù)需求選擇合適的組件組合,構(gòu)建功能強(qiáng)大且用戶友好的菜單系統(tǒng),感興趣的朋友跟隨小編一起看看吧2026-01-01
vue使用Print.js打印頁面樣式不出現(xiàn)的解決
這篇文章主要介紹了vue使用Print.js打印頁面樣式不出現(xiàn)的解決,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-10-10
Vite 打包目錄結(jié)構(gòu)自定義配置小結(jié)
在Vite工程開發(fā)中,默認(rèn)打包后的dist目錄資源常集中在asset目錄下,不利于資源管理,本文基于Rollup配置原理,本文就來介紹一下通過Vite配置自定義打包目錄結(jié)構(gòu),感興趣的可以了解一下2025-08-08

