Vue3内置组件

Vue 3 内置组件完全指南

本文档从零开始讲解 Vue 3 的内置组件Transition(单元素/组件过渡)、TransitionGroup(列表过渡)、KeepAlive(缓存组件)、Teleport(传送到其它 DOM)、Suspense(异步组件/异步 setup 的占位),配有大量示例,适合新手系统学习。


一、什么是“内置组件”?

1.1 概念

内置组件是 Vue 自带的、不需要你 import 就能在模板里直接用的特殊组件
它们名字首字母大写(如 TransitionKeepAlive),在模板里可以写 (Vue 会识别)。

1.2 有哪些?

内置组件 作用简述
Transition 单个元素或组件的进入/离开添加过渡动画
TransitionGroup 列表中元素的增删、顺序变化添加过渡
KeepAlive 缓存不显示的组件实例,切换回来时保留状态
Teleport 把组件渲染到 DOM 的其它位置(如 body)
Suspense 异步组件异步 setup 提供“加载中/失败”的占位

下面逐个详细说明和示例。


二、Transition:单元素/组件过渡

2.1 为什么需要?

v-ifv-show动态组件切换时,元素会瞬间出现/消失。
若希望出现时有淡入、离开时有淡出等动画,可以用 包住这个元素或组件,Vue 会在进入/离开的不同阶段自动加上/去掉你定义的 class,你只需写 CSS 或配合 JavaScript 钩子即可。

2.2 基本用法

包住一个根元素或一个组件(内部只能有一个根级子节点)。
当包住的节点被 v-ifv-show动态组件 等控制“显示/隐藏”时,Vue 会在进入离开时加上约定的 class,默认名字是 v-enter-fromv-enter-activev-enter-tov-leave-fromv-leave-activev-leave-to

示例:淡入淡出

<template>
  <div>
    <button @click="show = !show">切换</button>
    <Transition>
      <p v-if="show">你好,这是过渡内容</p>
    </Transition>
  </div>
</template>

<script setup>
import { ref } from 'vue'
const show = ref(true)
</script>

<style scoped>
.v-enter-active,
.v-leave-active {
  transition: opacity 0.3s ease;
}
.v-enter-from,
.v-leave-to {
  opacity: 0;
}
</style>
  • v-enter-from:进入的起始状态(还没进到最终位置/透明度)。
  • v-enter-active:进入过程,一般在这里写 transition
  • v-enter-to:进入的结束状态。
  • v-leave-from:离开的起始状态。
  • v-leave-active:离开过程。
  • v-leave-to:离开的结束状态(通常和 v-enter-from 一致,形成“淡出”)。

2.3 自定义 class 名前缀:name 属性

默认前缀是 v-,若想用 fade- 等,给 name=”fade”

<Transition name="fade">
  <p v-if="show">内容</p>
</Transition>
.fade-enter-active,
.fade-leave-active {
  transition: opacity 0.3s;
}
.fade-enter-from,
.fade-leave-to {
  opacity: 0;
}

2.4 与 v-show 一起用

Transition 也可以包 v-show,但 v-show 只是切换 display,离开时元素仍在 DOM 里,过渡仍会按“离开”阶段执行。

<Transition name="fade">
  <p v-show="show">内容</p>
</Transition>

2.5 动态组件切换

包住 时,组件切换会触发离开(旧组件)和进入(新组件)的过渡:

<Transition name="slide">
  <component :is="currentTab" />
</Transition>

2.6 只想要进入动画、不要离开动画

可以只写 enter 相关的 class,不写 leave,离开时就不会有过渡。
也可以配合 mode=”out-in”mode=”in-out” 控制“先离开再进入”或“先进入再离开”的顺序。

<Transition name="fade" mode="out-in">
  <component :is="current" />
</Transition>
  • mode=”out-in”:先执行离开动画,结束后再执行进入动画。
  • mode=”in-out”:先执行进入动画,再执行离开(较少用)。

2.7 使用 JavaScript 钩子(可选)

除了用 CSS class,还可以在 @before-enter@enter@after-enter@before-leave@leave@after-leave 里写逻辑,配合动画库(如 GSAP)做更复杂动画。
enterleave 里要在动画结束时调用 done(),否则 Vue 会一直等。

<Transition
  @before-enter="onBeforeEnter"
  @enter="onEnter"
  @after-enter="onAfterEnter"
>
  <p v-if="show">内容</p>
</Transition>
function onBeforeEnter(el) {
  el.style.opacity = 0
}
function onEnter(el, done) {
  el.animate([{ opacity: 0 }, { opacity: 1 }], { duration: 300 }).finished.then(done)
}
function onAfterEnter(el) {
  el.style.opacity = ''
}

三、TransitionGroup:列表过渡

3.1 为什么需要?

v-for 渲染的列表,当增删、排序时,希望每个项有进入/离开/移动的动画。
Transition 只能包一个根节点,不能直接包 v-forTransitionGroup 专门用来包列表,并为每个子元素应用过渡,同时支持 move(移动)动画。

3.2 基本用法

包住 v-for 的容器,并给每个列表项唯一的 key
TransitionGroup 默认会渲染成 ,可用 tag 改成 uldiv 等。

<template>
  <div>
    <button @click="add">添加</button>
    <button @click="remove">删除</button>
    <TransitionGroup name="list" tag="ul">
      <li v-for="item in list" :key="item.id">
        {{ item.text }}
      </li>
    </TransitionGroup>
  </div>
</template>

<script setup>
import { ref } from 'vue'
const list = ref([
  { id: 1, text: '项一' },
  { id: 2, text: '项二' }
])
function add() {
  list.value.push({ id: Date.now(), text: '新项' })
}
function remove() {
  list.value.pop()
}
</script>

<style scoped>
.list-enter-active,
.list-leave-active {
  transition: all 0.3s ease;
}
.list-enter-from,
.list-leave-to {
  opacity: 0;
  transform: translateX(30px);
}
.list-move {
  transition: transform 0.3s ease;
}
</style>
  • list-enter-from / list-leave-to:进入起点、离开终点。
  • list-move:列表项位置变化(重排)时的过渡,使“移动”有动画。

3.3 tag 与 tag 的样式

tag=”ul” 表示根元素渲染成

    ,注意自己给 ul 去默认 list-style,或给 TransitionGroupclass 写样式。


    四、KeepAlive:缓存组件

    4.1 为什么需要?

    v-if动态组件切换时,不显示的组件会被销毁,再切回来会重新创建,之前填的表、滚动位置都会丢。
    KeepAlive 会把已经渲染过的组件实例缓存在内存里,切走时只是隐藏、不销毁,切回来时直接复用,状态得以保留。

    4.2 基本用法

    包住动态组件v-if 控制的单个组件(通常配合 或多个 v-if/v-else-if 的组件)。

    <template>
      <div>
        <button @click="current = 'A'">Tab A</button>
        <button @click="current = 'B'">Tab B</button>
        <KeepAlive>
          <component :is="current" />
        </KeepAlive>
      </div>
    </template>
    
    <script setup>
    import { ref } from 'vue'
    import CompA from './CompA.vue'
    import CompB from './CompB.vue'
    const current = ref('A')
    </script>
    • 从 A 切到 B 再切回 A 时,A 的输入、滚动位置等会保留。
    • KeepAlive 只对直接子节点一个组件生效(多个子节点时只缓存第一个),所以一般只包一个

    4.3 include / exclude:只缓存部分组件

    include:只有名字匹配的组件会被缓存(组件 name 或注册名)。
    exclude:名字匹配的缓存。
    值可以是逗号分隔字符串正则数组

    <KeepAlive :include="['CompA', 'CompB']">
      <component :is="current" />
    </KeepAlive>
    <KeepAlive exclude="CompB">
      <component :is="current" />
    </KeepAlive>

    组件要有 name(在选项式里 export default { name: ‘CompA’ } 或在 里配合普通 name),include/exclude 才生效。

    4.4 max:最多缓存几个实例

    max 表示最多缓存多少个组件实例,超过时会把最久未使用的实例销毁。

    <KeepAlive :max="5">
      <component :is="current" />
    </KeepAlive>

    4.5 生命周期:onActivated / onDeactivated

    KeepAlive 缓存的组件,不会再触发 onUnmounted(因为没销毁),而是触发 onActivated(被重新显示时)和 onDeactivated(被隐藏时)。
    适合在 onActivated 里刷新数据、onDeactivated 里暂停轮询等。

    import { onActivated, onDeactivated } from 'vue'
    onActivated(() => {
      console.log('组件被激活(显示)')
    })
    onDeactivated(() => {
      console.log('组件被停用(隐藏)')
    })

    五、Teleport:传送到其它 DOM

    5.1 为什么需要?

    例如弹窗、Toast 要盖住整个页面,若写在某个深层组件里,会受父级的 overflow: hiddenz-index 等影响,不好做“全屏遮罩”。
    Teleport 可以把一段模板渲染到 body 或其它 DOM 节点,逻辑上仍在当前组件(仍可用当前组件的 props、emit、状态),只是 DOM 挂载点变了。

    5.2 基本用法

    包住要“传送”的内容,to CSS 选择器“body”

    <template>
      <div>
        <button @click="show = true">打开弹窗</button>
        <Teleport to="body">
          <div v-if="show" class="modal">
            <p>弹窗内容</p>
            <button @click="show = false">关闭</button>
          </div>
        </Teleport>
      </div>
    </template>
    
    <script setup>
    import { ref } from 'vue'
    const show = ref(false)
    </script>
    
    <style scoped>
    .modal {
      position: fixed;
      inset: 0;
      background: rgba(0, 0, 0, 0.5);
      display: flex;
      align-items: center;
      justify-content: center;
    }
    </style>
    • 点击“打开弹窗”后,modal 的 DOM 会出现在 下,不受父级样式影响。
    • show 仍是当前组件的 ref,逻辑不变。

    5.3 to 的值

    • “body”:挂到 document.body
    • “#app”:挂到 id 为 app 的节点。
    • “.container”:挂到第一个 class 为 container 的节点。
    • 要挂的节点必须已经存在于页面上(一般用 idbody)。

    5.4 条件性禁用传送:disabled

    disabled 为 true 时,不传送,子内容留在当前父节点下渲染。

    <Teleport to="body" :disabled="isMobile">
      <div class="modal">...</div>
    </Teleport>

    六、Suspense:异步组件与异步 setup(实验性)

    6.1 为什么需要?

    异步组件(动态 import)或异步 setup(async setup)在“加载中”时,若没有占位,页面可能空白或布局闪动。
    Suspense 提供两个插槽:#default(真正要渲染的异步内容)和 #fallback(加载中显示的占位),Vue 会在异步未完成时显示 fallback,完成后显示 default

    6.2 基本用法

    包住异步组件内部有异步 setup 的组件,并提供一个 #fallback

    <template>
      <Suspense>
        <template #default>
          <AsyncComp />
        </template>
        <template #fallback>
          <p>加载中...</p>
        </template>
      </Suspense>
    </template>
    
    <script setup>
    import { defineAsyncComponent } from 'vue'
    const AsyncComp = defineAsyncComponent(() => import('./AsyncComp.vue'))
    </script>
    • AsyncComp 加载完成前显示“加载中…”,完成后显示 AsyncComp
    • AsyncCompsetupasync 且返回 Promise,Suspense 也会等该 Promise resolve 后再显示 #default

    6.3 与 defineAsyncComponent 配合

    defineAsyncComponent 用来定义“异步加载”的组件,可配合 loadingComponentdelaytimeout 等;和 Suspense 一起用时,Suspense#fallback 就是“加载中”的 UI。

    const AsyncComp = defineAsyncComponent({
      loader: () => import('./Heavy.vue'),
      delay: 200,
      timeout: 3000
    })

    注意:Suspense 在 Vue 3 中仍被视为实验性,API 可能调整,生产环境使用前建议查官方文档确认。


    七、内置组件速查表

    内置组件 作用 常用属性/插槽
    Transition 单元素/组件进入离开过渡 name、mode;CSS:v-enter-from/active/to、v-leave-from/active/to
    TransitionGroup 列表增删、排序过渡 name、tag;CSS:xxx-move
    KeepAlive 缓存组件实例 include、exclude、max;子组件有 onActivated/onDeactivated
    Teleport 把内容渲染到其它 DOM to(选择器或 “body”)、disabled
    Suspense 异步组件/async setup 占位 #default、#fallback

    八、学习建议

    1. Transition:先会写 name + 六段 class(enter-from/active/to、leave-from/active/to),再做“淡入淡出”“滑动”等效果;需要再学 mode、JavaScript 钩子。
    2. TransitionGroup:包 v-for 列表,设 key,写 xxx-move 让重排有动画。
    3. KeepAlive:包 ,需要时加 include/exclude/max,在子组件里用 onActivated/onDeactivated
    4. Teleport:弹窗、Toast 等用 to=”body” 挂到 body,避免被父级裁剪。
    5. Suspense:异步组件或 async setup 时用 #default + #fallback,注意其实验性。

    把本文档里的 TransitionKeepAliveTeleport 示例在项目里敲一遍,会掌握得更牢。祝你学习顺利。

发表评论