Vue Transition 和 TransitionGroup 有什么区别?
简化版
Transition 处理单个元素或组件的进入、离开动画;TransitionGroup 处理列表中多个元素的进入、离开和位置移动动画。前者常用于弹窗和折叠,后者常用于列表增删排序。
详细版
Transition 会在元素进入和离开时添加一组 CSS class。
<Transition name="fade">
<div v-if="open">内容</div>
</Transition>
TransitionGroup 面向 v-for 列表,每个子节点必须有稳定 key。
<TransitionGroup name="list" tag="ul">
<li v-for="item in items" :key="item.id">{{ item.name }}</li>
</TransitionGroup>
面试重点是区分使用对象:单节点用 Transition,列表用 TransitionGroup;列表动画尤其依赖稳定 key,否则 Vue 无法正确判断哪个元素进入、离开或移动。
完整版教学
一、过渡组件解决的是状态变化的视觉连续性
页面状态变化如果直接切换,用户会感觉元素突然出现或消失。 过渡动画的价值不是炫技,而是让用户理解界面发生了什么。 例如弹窗淡入说明“出现一个临时层”,列表项滑出说明“这一项被删除”。
无动画:
状态改变 -> DOM 立即变化
有过渡:
状态改变 -> 进入/离开过程 -> DOM 完成变化
如果一个列表删除 1 项后高度瞬间塌陷,用户可能不知道哪项消失。 给 200ms 的离场动画,认知负担会小很多。
二、Transition 适合单个节点的进入和离开
Transition 包裹一个元素或组件。
当 v-if、动态组件或路由组件切换时,Vue 会在合适时机添加进入和离开的 class。
开发者只需要写 CSS。
<Transition name="fade">
<div v-if="open" class="modal">弹窗</div>
</Transition>
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.2s ease;
}
.fade-enter-from,
.fade-leave-to {
opacity: 0;
}
这里的数字 0.2s 常用于小型浮层。
太短用户感知不到,太长会拖慢操作反馈。
三、TransitionGroup 面向列表,key 是生命线
列表动画和单节点动画最大的不同是:Vue 必须知道每个元素的身份。
TransitionGroup 依赖 key 判断哪个元素新增、哪个删除、哪个移动。
如果用 index 做 key,排序或插入时身份会错乱,动画也会错。
<TransitionGroup name="list" tag="ul">
<li v-for="todo in todos" :key="todo.id">
{{ todo.text }}
</li>
</TransitionGroup>
| 能力 | Transition | TransitionGroup |
|---|---|---|
| 单节点进入/离开 | 适合 | 不主要 |
| 列表新增/删除 | 不适合 | 适合 |
| 列表移动动画 | 不支持 | 支持 |
| key 要求 | 动态组件常需要 | 必须稳定 |
假设列表有 10 项,在第 2 项插入新数据。 稳定 id 能让 Vue 知道只有 1 项新增、后面 8 项移动;index key 会让身份映射变混乱。
四、过渡 class 有固定阶段
Vue 的 CSS 过渡 class 分为 from、active、to。
进入时从 enter-from 到 enter-to,离开时从 leave-from 到 leave-to。
active 控制整个过渡期间的 transition 或 animation。
进入:
enter-from -> enter-active -> enter-to
离开:
leave-from -> leave-active -> leave-to
理解这个流程后,写动画就不是背类名。 from 是起点,to 是终点,active 是怎么从起点走到终点。 比如淡入就是 opacity 从 0 到 1,滑入就是 transform 从偏移到归位。
五、移动动画依赖 FLIP 思路
TransitionGroup 的移动动画常和 FLIP 思路有关。
浏览器先记录元素旧位置,再更新 DOM 得到新位置,最后通过 transform 让元素从旧位置平滑移动到新位置。
这比手动计算每个元素怎么移动要简单。
.list-move {
transition: transform 0.3s ease;
}
First: 记录旧位置
Last: 计算新位置
Invert: 反向偏移
Play: 播放回新位置
如果一次排序让 20 个元素移动,FLIP 能避免大量手写动画逻辑。 但动画仍有成本,超长列表不应该给每一项都做复杂过渡。
六、动画要兼顾性能和可访问性
前端动画优先使用 opacity 和 transform,尽量避免频繁触发布局的属性,如 height、top、left。
复杂列表动画可能造成掉帧。
还要尊重用户的减少动态效果偏好。
@media (prefers-reduced-motion: reduce) {
.fade-enter-active,
.fade-leave-active {
transition: none;
}
}
记忆钩子:Transition 管一个演员进退场,TransitionGroup 管一队演员换位置。
这句能帮助你快速选择组件。 如果面试官继续追问,就补 key、class 阶段、transform 性能和列表移动。
七、常见误区与追问
- 误区:TransitionGroup 可以不写 key。 列表过渡必须有稳定 key,否则无法识别元素身份和移动关系。
- 误区:动画越复杂越能体现能力。 工程动画要服务理解和反馈,过度动画会增加性能和可访问性负担。
- 误区:所有 CSS 属性都适合动画。 优先使用 opacity 和 transform,谨慎动画化会触发布局的属性。
- 追问:Transition 的 class 阶段有哪些? 进入和离开都包含 from、active、to 三类阶段。
- 追问:TransitionGroup 为什么能做移动动画? 它能基于元素位置变化添加 move class,常体现 FLIP 思路。
- 追问:弹窗适合用哪个? 单个弹窗用 Transition,多个通知列表或排序列表用 TransitionGroup。
八、加强记忆
这题按“单个、列表、性能”记。单节点进出用 Transition,列表增删移动用 TransitionGroup,列表必须稳定 key;CSS 阶段看 from/active/to;动画属性优先 opacity 和 transform,并考虑减少动态效果偏好。