Vue 动态组件(Dynamic Components)
动态组件是 Vue 中一个非常实用的特性,它允许我们在同一个挂载点(一个 <component> 元素)上动态地切换不同的组件。这种机制使得组件的渲染逻辑更加灵活,尤其在需要根据用户交互或应用状态改变视图时非常有用。
什么是动态组件
简单来说,动态组件就是通过一个特殊的 <component> 元素,并绑定其 is 属性来决定当前要渲染的组件 。is 属性的值可以是一个已注册的组件名,也可以是一个导入的组件对象。
当 is 的值发生变化时,Vue 就会销毁旧的组件实例并用新的组件替换。
基本用法
使用 <component> 元素
在模板中,使用 <component> 标签,并通过 :is 绑定要渲染的组件:
vue
<template>
<component :is="currentComponent"></component>
</template>
<script setup>
import { ref } from 'vue'
import ComponentA from './ComponentA.vue'
import ComponentB from './ComponentB.vue'
const currentComponent = ref(ComponentA) // 也可以使用组件名 'ComponentA'
</script>
:is 的两种绑定方式
-
绑定组件对象(推荐):直接导入组件并绑定。
vue<script setup> import { ref } from 'vue' import ComponentA from './ComponentA.vue' import ComponentB from './ComponentB.vue' const currentComponent = ref(ComponentA) // 也可以使用组件名 'ComponentA' </script> -
绑定组件名称字符串 :组件必须在
components选项中注册(选项式API)或全局注册。vue<script> // 选项式 API 示例 export default { data() { return { current: 'MyComponent' } }, components: { MyComponent } } </script>
实际示例:通过按钮切换组件
vue
<template>
<div>
<button
v-for="tab in tabs"
:key="tab"
@click="currentTab = tab"
:class="{ active: currentTab === tab }"
>
{{ tab }}
</button>
<component :is="currentTabComponent" class="tab-content"></component>
</div>
</template>
<script setup>
import { ref, computed } from 'vue'
import HomeTab from './HomeTab.vue'
import PostsTab from './PostsTab.vue'
import ArchiveTab from './ArchiveTab.vue'
const tabs = ['Home', 'Posts', 'Archive']
const currentTab = ref('Home')
const currentTabComponent = computed(() => {
switch (currentTab.value) {
case 'Home': return HomeTab
case 'Posts': return PostsTab
case 'Archive': return ArchiveTab
default: return HomeTab
}
})
</script>
使用 <keep-alive> 缓存组件状态
默认情况下,每次切换动态组件,Vue 都会销毁旧组件并创建新组件,这意味着组件内部的状态会丢失。如果我们希望保留组件的状态(例如表单输入内容、滚动位置等),可以将 <component> 包裹在 <keep-alive> 标签内。
vue
<template>
<keep-alive>
<component :is="currentTabComponent"></component>
</keep-alive>
</template>
这样,被切换掉的组件会被缓存,而不是销毁。当再次切换回来时,组件会从缓存中恢复,保留之前的状态。
按条件缓存
<keep-alive> 还支持 include 和 exclude 属性,用于指定哪些组件需要被缓存(通过组件名称匹配)。
vue
<keep-alive include="HomeTab,PostsTab">
<component :is="currentTabComponent"></component>
</keep-alive>
动态组件与异步组件结合
当应用较大时,我们可以结合 Vue 的异步组件来按需加载,提高首屏加载速度。
vue
<template>
<component :is="asyncComponent"></component>
</template>
<script setup>
import { ref, defineAsyncComponent } from 'vue'
const asyncComponent = ref(null)
// 假设在某个时机加载组件
function loadComponent() {
asyncComponent.value = defineAsyncComponent(() =>
import('./HeavyComponent.vue')
)
}
</script>
动态组件的 is 属性可以直接接收一个异步组件工厂函数,Vue 会在需要渲染时自动解析它。
注意事项
is 属性的绑定方式(Vue 2 vs Vue 3)
- Vue 3 :直接使用
:is="组件对象/名称",无需额外指令。 - Vue 2 :动态组件的
is属性通常写作:is="componentName",但如果想直接传入组件对象,需要使用is属性并配合v-bind。
避免使用 HTML 元素名作为组件名
在 Vue 3 中,如果将 is 绑定到一个 HTML 标签名(如 'div'),Vue 会将其渲染为普通 HTML 元素,而不是 Vue 组件。这通常用于在原生元素上动态切换标签,但如果你想要的是 Vue 组件,请确保绑定的值是组件对象或已注册的组件名称。
XSS 防范
永远不要将用户可编辑的内容直接作为 is 的值(例如通过 v-html 或拼接字符串),否则可能导致 XSS 攻击。应当始终使用受控的组件名或组件对象。
与 v-if / v-else 的选择
- 如果只有少数几个固定组件的切换,使用
v-if/v-else-if/v-else也可以。 - 当组件的数量不确定或需要动态变化时,动态组件更加简洁。