CSS过渡类名详解:从v-enter到自定义过渡

说实话,刚接触Vue过渡的时候,我也被那一堆类名搞晕过。v-enter、v-enter-active、v-enter-to……这到底谁先谁后?什么时候触发?今天咱们就把这事彻底捋清楚。

v-enter/v-leave类名的生命周期

Vue的过渡系统,说白了就是一套CSS类名的自动切换机制。当元素插入或移除时,Vue会在合适的时机加上或去掉特定的类名。

来看一个典型的进入动画流程:

/* 进入前 - 元素还没出现 */
.v-enter {
  opacity: 0;
  transform: translateY(20px);
}

/* 进入过程中 - 整个动画期间都生效 */
.v-enter-active {
  transition: all 0.3s ease;
}

/* 进入结束 - 元素完全显示后的状态 */
.v-enter-to {
  opacity: 1;
  transform: translateY(0);
}

这里有个关键点,我刚开始学的时候踩过坑:v-enter 和 v-enter-to 是互斥的。元素刚插入时,v-enter生效,下一帧v-enter被移除,v-enter-to被加上。如果你在v-enter里设置了opacity: 0,又在v-enter-to里设置了opacity: 1,那transition就会从0过渡到1。

生命周期时间线(进入动画):

  • 第0帧:元素插入DOM,v-enter和v-enter-active同时被添加
  • 第1帧:v-enter被移除,v-enter-to被添加
  • 过渡结束后:v-enter-active和v-enter-to被移除

离开动画正好反过来:

/* 离开前 - 元素还在 */
.v-leave {
  opacity: 1;
}

/* 离开过程中 */
.v-leave-active {
  transition: all 0.3s ease;
}

/* 离开结束 - 元素消失 */
.v-leave-to {
  opacity: 0;
}

嗯,这里要注意:v-leave 其实很少用到。因为元素离开前就是它原本的样子,你通常不需要再声明一遍。我个人的习惯是,离开动画只写 v-leave-active 和 v-leave-to。

自定义过渡类名

默认的 v- 前缀虽然方便,但遇到多个不同动画时就尴尬了。总不能所有过渡都用同一套样式吧?

Vue提供了自定义类名的方案,通过 transition 组件的 props 传入:

<transition
  enter-class="fade-enter"
  enter-active-class="fade-enter-active"
  enter-to-class="fade-enter-to"
  leave-class="fade-leave"
  leave-active-class="fade-leave-active"
  leave-to-class="fade-leave-to"
>
  <div v-if="show">自定义过渡</div>
</transition>

这样你就可以写自己的类名了:

.fade-enter {
  opacity: 0;
}
.fade-enter-active {
  transition: opacity 0.5s;
}
.fade-enter-to {
  opacity: 1;
}

我在项目中经常用这个特性来区分不同组件的动画。比如弹窗用 slide-fade,列表用 list-enter,互不干扰。

小技巧:如果你用CSS预处理器(SCSS/LESS),可以把过渡类名定义成mixin,复用起来特别爽。

过渡模式:in-out 和 out-in

默认情况下,Vue的进入和离开动画是同时进行的。这会导致一个问题——两个元素同时存在,视觉上很乱。

举个例子,一个切换按钮:

<transition mode="out-in">
  <button :key="currentTab">
    {{ currentTab }}
  </button>
</transition>

这里我用了 mode="out-in",意思是:先让旧元素离开,再让新元素进入。这样就不会出现两个按钮重叠的尴尬情况。

Vue提供了三种模式:

模式 行为 适用场景
默认(同时) 进入和离开同时进行 简单的淡入淡出
in-out 新元素先进入,旧元素再离开 很少用,容易造成布局抖动
out-in 旧元素先离开,新元素再进入 最常用,切换体验流畅

说实话,in-out 模式我几乎没用过。你想啊,新元素先进来,旧元素还在,两个同时存在,除非你设计的就是叠加效果,否则看起来会很奇怪。

避坑指南:我曾经在列表切换时忘了加 mode,结果两个列表同时渲染,DOM结构都乱了。后来养成了习惯——只要涉及元素替换,必加 mode="out-in"。

实际项目中的组合用法

把上面这些知识点串起来,看看一个完整的例子:

<template>
  <transition
    name="custom"
    mode="out-in"
    enter-active-class="animate__animated animate__fadeIn"
    leave-active-class="animate__animated animate__fadeOut"
  >
    <div :key="page" class="page-content">
      {{ page }}
    </div>
  </transition>
</template>

这里我用了 Animate.css 的类名作为自定义过渡类名,配合 out-in 模式,页面切换效果非常丝滑。你想想看,如果不用自定义类名,每个动画都得手写,多累啊。

最后总结一下我的经验:

  • v-enter/v-leave:记住它们的生命周期,尤其是第0帧和第1帧的区别
  • 自定义类名:多组件项目必备,避免样式冲突
  • 过渡模式:out-in 是首选,in-out 慎用

掌握了这些,Vue过渡的基础你就拿下了。下一章咱们聊聊 JavaScript 钩子,那才是真正玩出花的地方。