人人都会AI编程

<component :is> 动态切换组件

更新时间:2026-07-09

很多页面上都有这样的区域:根据当前选中的标签页(Tab),显示不同的内容组件。比如“账户设置”页面,点击“个人资料”显示 ProfileForm,点击“修改密码”显示 PasswordForm,点击“通知偏好”显示 NotificationSettings

传统的做法是写一堆 v-if / v-else 手动判断:

<ProfileForm v-if="currentTab === 'profile'" />
<PasswordForm v-else-if="currentTab === 'password'" />
<NotificationSettings v-else-if="currentTab === 'notifications'" />

这种写法在选项少的时候还行,一旦 Tab 数量多起来,或者组件需要被其他页面复用,维护起来就很痛苦。

Vue 提供了一个更优雅的方式——内置的 <component> 元素配合 :is 属性,可以动态决定当前渲染哪个组件。

基础用法

<template>
  <div>
    <!-- 切换按钮 -->
    <button @click="current = 'Home'">首页</button>
    <button @click="current = 'About'">关于</button>

    <!-- 动态渲染 -->
    <component :is="current" />
  </div>
</template>

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

const current = ref('Home')
</script>

:is 的值可以是:

  • 已注册的全局或局部组件的名字(字符串,如上例)
  • 导入的组件选项对象(直接传入组件对象本身)
  • 一个异步组件defineAsyncComponent 返回的结果)

也就是说,你可以在同一个挂载点上,根据数据的变化无缝切换不同的组件,不需要任何额外的条件判断。

实际场景:Tab 栏切换

最常见的场景就是 Tab 导航。把所有的 Tab 面板组件映射到一个对象里,配合 :is 就能干净地搞定:

<template>
  <div>
    <nav>
      <button
        v-for="tab in tabs"
        :key="tab.name"
        @click="activeTab = tab.name"
        :class="{ active: activeTab === tab.name }"
      >
        {{ tab.label }}
      </button>
    </nav>

    <!-- 动态渲染当前选中 Tab 对应的组件 -->
    <component :is="componentMap[activeTab]" />
  </div>
</template>

<script setup>
import { ref } from 'vue'
import ProfileForm from './ProfileForm.vue'
import PasswordForm from './PasswordForm.vue'
import Notifications from './Notifications.vue'

const tabs = [
  { name: 'profile', label: '个人资料' },
  { name: 'password', label: '修改密码' },
  { name: 'notifications', label: '通知' }
]
const activeTab = ref('profile')

// 组件映射表
const componentMap = {
  profile: ProfileForm,
  password: PasswordForm,
  notifications: Notifications
}
</script>

这个结构清晰、扩展性强:未来新增一个 Tab,只需在 componentMap 里补一行映射,在 tabs 数组里加一条记录即可,模板完全不需要改动。

与 keep-alive 配合:保留组件状态

动态切换组件时,默认情况下每次切换都会销毁当前组件并创建新组件。比如用户在 ProfileForm 中填了一半的表单,不小心切到 PasswordForm 再切回来,之前的填写内容全部丢失。

这时可以加上 Vue 内置的 <keep-alive> 组件包裹 <component>,让被切换走的组件被缓存,而不是销毁:

<keep-alive>
  <component :is="activeComponent" />
</keep-alive>

加了这个包裹之后,ProfileForm 的状态会被保留,切回来时表单数据还在。如果你只想缓存特定组件,可以使用 includeexclude 属性按名字筛选。

注意事项

  • :is 支持组件名但推荐直接传组件对象。传字符串依赖全局注册,如果组件只在当前页面导入,传字符串会找不到。直接传导入的组件变量(如上例中的 Home)是最可靠的方式,也能获得 TypeScript 类型检查。
  • 动态组件切换时会触发完整的生命周期。从 onUnmountedonMounted,除非使用了 keep-alive,这时会触发 onActivated / onDeactivated 替代。
  • :is 也可以渲染普通 HTML 元素,例如 <component :is="'h1'">,但极少场景会这样用,核心价值还是动态切换业务组件。

动态组件让 Vue 的组件树可以根据数据轻松重组,配合 keep-alive 缓存还能兼顾性能与用户体验,是处理条件渲染的“官方推荐模式”。