Skip to content

Vue Transition 实战 ​

是什么 ​

Vue 内置的 <Transition> 组件:把「元素插入/移除」和「CSS 动画」自动接起来。它解决的问题是:JS 里 v-if 移除元素的瞬间,CSS 动画来不及播——Transition 会等动画播完才真正移除。

这是 Vue 相对 React 的原生优势(React 要装第三方库才有同等体验),做交互体验值得用好它。

最小示例:淡入淡出 ​

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

<template>
  <button @click="show = !show">切换</button>

  <Transition name="fade">
    <p v-if="show">我被优雅地淡入淡出</p>
  </Transition>
</template>

<style>
/* name="fade" → 自动接上 .fade-enter-* / .fade-leave-* 这组类 */
.fade-enter-active,
.fade-leave-active { transition: opacity 250ms ease, transform 250ms ease; }

.fade-enter-from   { opacity: 0; transform: translateY(8px); } /* 进场起点 */
.fade-leave-to     { opacity: 0; transform: translateY(-8px); } /* 离场终点 */
</style>

类名规律(name="fade" 时):

类名时机
fade-enter-from进场起点(刚插入)
fade-enter-active进场全程(写 transition/animation 在这)
fade-leave-active离场全程
fade-leave-to离场终点

实战一:模态框(遮罩 + 面板双层动画) ​

vue
<Transition name="modal">
  <div v-if="open" class="mask" @click="open = false">
    <div class="panel" @click.stop>…</div>
  </div>
</Transition>

<style>
.mask { position: fixed; inset: 0; background: rgba(0,0,0,.4); display: grid; place-items: center; }
.mask, .panel { transition: all 300ms cubic-bezier(.22,1,.36,1); }
.panel { background: #fff; border-radius: 12px; padding: 24px; }

.modal-enter-from, .modal-leave-to { opacity: 0; }
.modal-enter-from .panel, .modal-leave-to .panel { transform: translateY(16px) scale(.98); }
/* 面板比遮罩多动一点:错落感 */
.modal-leave-active { transition: all 200ms ease-in; } /* 关比开快(退场要快) */
</style>

实战二:列表增删(TransitionGroup) ​

vue
<TransitionGroup name="list" tag="ul">
  <li v-for="item in items" :key="item.id">{{ item.text }}</li>
</TransitionGroup>

<style>
.list-enter-active, .list-leave-active { transition: all 300ms ease; }
.list-enter-from { opacity: 0; transform: translateX(-16px); }
.list-leave-to   { opacity: 0; transform: translateX(16px); }
.list-move       { transition: transform 300ms ease; } /* 其他元素平滑让位 */
</style>

关键三点:tag="ul" 指定渲染成列表标签;:key 必须是稳定唯一 id(别用数组下标);.list-move 让「被删项旁边的元素」平滑补位——不加它会让位生硬。

实战三:路由切换淡入 ​

vue
<!-- App.vue -->
<router-view v-slot="{ Component }">
  <Transition name="page" mode="out-in">
    <component :is="Component" />
  </Transition>
</router-view>

<style>
.page-enter-active, .page-leave-active { transition: opacity .2s ease; }
.page-enter-from, .page-leave-to { opacity: 0; }
</style>

mode="out-in":旧页面先退场再进新页面,避免两页同帧叠加跳动。

注意事项 ​

四个坑

  1. <Transition> 里只能有一个根元素(v-if / v-else 二选一可以)
  2. CSS 动画用 animation 而非 transition 时,active 类里写 animation 即可,Vue 会自动监听 animationend
  3. 初次渲染也想播动画:加 appear 属性
  4. transition 写在 enter-from 里不生效——它只该写「起点状态」,过渡声明写在 *-active

延伸:什么时候升级到动画库 ​

内置 Transition 覆盖 90% 场景。需要复杂时间线编排(多元素序列、滚动驱动、物理弹簧)时再考虑:

  • GSAP:跨框架通用的时间线动画库,复杂编排的事实标准
  • @vueuse/motion:Vue 生态的声明式动效

原则:先内置,后库。精选资源里有链接。