transition 必须写在初始状态,不能只写在 hover 或 v-if 切换后;需在初始样式中声明 transition 属性,否则首次变化无动画;scoped 下需用 /deep/ 或 ::v-deep 穿透;transition 子节点必须唯一;必须配合 v-if 而非 v-show 使用。

transition 必须写在初始状态,不能只写在 hover 或 v-if 切换后
Vue 的 <transition> 本身不控制 CSS 动画逻辑,它只是按阶段给元素加类名(如 v-enter-active、fade-leave-to)。这些类名要生效,对应 CSS 规则里的 transition 属性必须已存在于元素的「初始渲染样式」中。
常见错误是把 transition: opacity 0.3s; 只写在 .fade-enter-active 里,而没写在默认状态(比如 .fade-enter 或基础类)上。结果就是:元素刚插入时没有过渡定义,浏览器直接跳到目标值。
- ✅ 正确做法:在初始状态(哪怕只是空规则)中声明
transition,例如:.fade-enter, .fade-leave-to { opacity: 0; }<br>.fade-enter-active, .fade-leave-active {<br> transition: opacity 0.3s ease;<br>} - ❌ 错误写法:
.fade-enter-active { transition: opacity 0.3s; }单独存在,但.fade-enter没定义任何样式 → 浏览器认为该元素初始无transition,首次变化不触发动画 - 注意:Vue2 默认类名是
v-enter/v-leave,2.1.8+ 推荐用v-enter-from/v-leave-from,但无论哪套,-active类只负责「过程」,不是「起点」
scoped 样式下 .fade-* 类名匹配失败
在 Vue2 的 <style scoped> 中,所有类名会被自动加上 data-v-xxxx 属性选择器。但 <transition> 添加的 fade-enter-active 等类名是运行时动态加上的,不带这个哈希 —— 导致样式完全不命中。
这不是 Vue Bug,而是 scoped 机制的必然行为。不解决就永远看不到动画。
立即学习“前端免费学习笔记(深入)”;
- ✅ 解决方案:用深度选择器穿透 scoped 限制,推荐写法:
/deep/ .fade-enter-active,<br>/deep/ .fade-leave-active {<br> transition: opacity 0.3s;<br>} - ⚠️ 注意:
/deep/在某些构建配置中可能被弃用,可改用::v-deep .fade-enter-active(需确认 loader 支持) - 别用
<style>不加 scoped —— 容易污染全局;也别把动画样式提到父组件,破坏封装性
transition 包裹的不是单个根元素
Vue 的 <transition> 要求其**直接子节点必须是且仅是一个 DOM 元素或一个返回单个根节点的组件**。如果子节点是多个标签、文本节点、注释,或组件内部返回了 Fragment(Vue2 不支持多根),就会报错或静默失效。
典型报错如 Vue3 的 "non-element root node",但在 Vue2 中往往表现为动画不触发、类名不添加、甚至控制台无提示。
- ✅ 检查方式:打开开发者工具 Elements 面板,确认
<transition>下是否只有一个顶层元素(如<div>) - ❌ 常见错误写法:
<transition name="fade"><br> <h2>标题</h2><br> <p>内容</p><br></transition>
→ 两个兄弟节点,无效 - ✅ 修复:用一个容器包裹,例如:
<transition name="fade"><br> <div><br> <h2>标题</h2><br> <p>内容</p><br> </div><br></transition>
用了 v-show 而不是 v-if
v-show 是通过切换 display: none 控制显隐,DOM 始终存在;而 <transition> 的 enter/leave 生命周期只在元素**插入或移除 DOM** 时触发 —— 所以 v-show 下,enter 和 leave 阶段根本不会运行。
你看到的“没动画”,其实是根本没有进入 transition 流程。
- ✅ 必须用
v-if(或v-else)配合<transition>,例如:<transition name="fade" mode="out-in"><br> <div v-if="tab === 'info'">信息页</div><br> <div v-else>详情页</div><br></transition>
- ⚠️
mode="out-in"很关键:避免两个 tab 同时存在导致布局抖动 - 别试图给
v-show元素加transition类名 —— 它不走 Vue 过渡系统,只能靠纯 CSS + JS 监听 display 变化,得不偿失
最容易被忽略的是:transition 生效与否,和“有没有写”关系不大,而取决于「类名是否匹配」「DOM 是否真正进出」「初始样式是否存在 transition 声明」这三件事是否同时满足。尤其在 scoped 场景下,漏掉 /deep/ 几乎是必现问题,但控制台又不报错,排查起来最耗时间。


















