第二章:单元素/组件的过渡——<transition>组件基础、v-if/v-show过渡、动态切换过渡效果
好,咱们进入正题。单元素或组件的过渡,是 Vue 动画体系里最基础、也最常用的一块。说白了,就是控制一个元素「怎么出现」和「怎么消失」。你想想看,页面上的弹窗、提示、切换标签,哪个不需要这种效果?
我个人习惯把这一章当作 Vue 动画的「敲门砖」。搞懂了它,后面那些复杂的列表动画、状态动画,理解起来就顺多了。
2.1 <transition> 组件:Vue 动画的基石
Vue 提供了一个内置组件叫 <transition>。它不渲染任何 DOM 元素,只负责在元素插入或移除时,自动添加/移除 CSS 类名。你只需要写好 CSS,动画就出来了。
我在项目中遇到过不少新手,直接给元素写 transition: all 0.3s,然后发现用 v-if 控制显示隐藏时,动画根本不生效。为什么?因为 v-if 是直接销毁和重建 DOM,浏览器没有机会执行过渡。而 <transition> 就是来解决这个问题的。
基本用法长这样:
<template>
<button @click="show = !show">切换</button>
<transition name="fade">
<p v-if="show">Hello Vue Animation</p>
</transition>
</template>
<script>
export default {
data() {
return { show: true }
}
}
</script>
<style>
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.5s ease;
}
.fade-enter-from,
.fade-leave-to {
opacity: 0;
}
</style>
这里的关键是 name="fade"。Vue 会自动生成六个 CSS 类名:
| 阶段 | 类名 | 说明 |
|---|---|---|
| 进入开始 | fade-enter-from |
元素插入前的初始状态 |
| 进入激活 | fade-enter-active |
整个进入过程的过渡效果 |
| 进入结束 | fade-enter-to |
元素插入后的最终状态 |
| 离开开始 | fade-leave-from |
离开前的初始状态 |
| 离开激活 | fade-leave-active |
整个离开过程的过渡效果 |
| 离开结束 | fade-leave-to |
离开后的最终状态 |
name 属性,默认类名前缀是 v-。比如 v-enter-from。我个人建议总是显式设置 name,避免多个过渡组件冲突。
2.2 v-if 与 v-show 的过渡差异
嗯,这里要注意一个常见的坑。很多人以为 v-if 和 v-show 用 <transition> 包起来效果一样。其实不然。
v-if:元素会被真正销毁和重建。过渡效果完整,进入和离开动画都会执行。v-show:元素始终存在,只是切换display属性。离开动画会执行,但进入动画可能「一闪而过」。
为什么会这样?因为 v-show 在切换时,元素并没有从 DOM 中移除。Vue 的 <transition> 机制依赖于元素的插入和移除事件。对于 v-show,它只能触发 display 的变化,而 display: none 到 display: block 的切换,浏览器不会自动触发 CSS 过渡。
我曾经在一个后台管理系统里,用 v-show 控制侧边栏的展开收起,结果发现展开时动画总是「卡一下」。排查了半天,才发现是 v-show 的问题。后来改成 v-if 配合 <transition>,效果就流畅了。
如果你确实需要用 v-show 做过渡,可以这样处理:
<transition name="slide">
<div v-show="visible" class="panel">
内容区域
</div>
</transition>
<style>
.slide-enter-active,
.slide-leave-active {
transition: all 0.3s ease;
}
.slide-enter-from,
.slide-leave-to {
opacity: 0;
transform: translateX(-20px);
}
/* 关键:强制初始状态 */
.panel {
display: block !important;
}
</style>
v-show 做过渡时,记得给元素加上 display: block !important,否则 Vue 的过渡类名可能被 v-show 的内联样式覆盖。我曾经因为这个 bug 被测试小姐姐追着问了一下午...
2.3 动态切换过渡效果
有时候,我们需要根据不同的场景使用不同的过渡效果。比如:从左侧滑入用 slide-left,从右侧滑入用 slide-right。Vue 的 <transition> 组件支持通过 name 属性动态绑定。
<template>
<button @click="direction = 'left'">从左进入</button>
<button @click="direction = 'right'">从右进入</button>
<transition :name="transitionName">
<p v-if="show" key="content">
当前过渡方向:{{ direction }}
</p>
</transition>
</template>
<script>
export default {
data() {
return {
show: true,
direction: 'left'
}
},
computed: {
transitionName() {
return this.direction === 'left' ? 'slide-left' : 'slide-right'
}
}
}
</script>
<style>
.slide-left-enter-active,
.slide-left-leave-active {
transition: all 0.3s ease;
}
.slide-left-enter-from {
transform: translateX(-100%);
opacity: 0;
}
.slide-left-leave-to {
transform: translateX(100%);
opacity: 0;
}
.slide-right-enter-active,
.slide-right-leave-active {
transition: all 0.3s ease;
}
.slide-right-enter-from {
transform: translateX(100%);
opacity: 0;
}
.slide-right-leave-to {
transform: translateX(-100%);
opacity: 0;
}
</style>
这里有个细节:我加了一个 key="content"。为什么?因为 Vue 在复用相同标签元素时,会尽量复用 DOM 节点。如果不加 key,切换方向时可能不会触发重新渲染,过渡效果就出不来了。你想想看,Vue 觉得「嗯,还是那个 p 标签,不用动」,那动画自然就没了。
动态切换过渡效果在实际项目中非常实用。我记得做过一个多步骤表单,每一步的切换方向不同——下一步从左滑入,上一步从右滑入。用动态 name 配合 key,几行代码就搞定了。
<transition>只对单个元素/组件生效- 必须配合
v-if、v-show或动态组件使用 - 动态
name可以实现不同场景的过渡切换 - 相同标签元素记得加
key,否则动画可能不触发
好了,这一章的内容就这些。说白了就是三件事:用 <transition> 包住元素、写好 CSS 类名、根据场景动态切换。下一章我们会聊多元素过渡和列表过渡,到时候你会发现,基础打牢了,复杂的东西其实也没那么难。