直接用 transition 无法对 li 元素实现增删动画,因其仅响应 css 属性数值变化,不感知 dom 节点插入或移除;新增项无起始状态、删除项无结束过程,故无法触发 transitionend。

直接用 transition 做列表项进出场动画,行不通——除非你放弃 DOM 增删,改用显隐控制 + 手动管理类名,且只动 opacity 和 transform。
为什么不能对 li 元素直接用 transition 控制增删?
transition 只响应 CSS 属性的数值变化,不感知 DOM 节点的插入或移除。当你用 JavaScript 调用 list.push() 或 list.splice() 触发 Vue/React 渲染,新 li 是“突然出现”的,没有起始状态;被删的 li 则是“瞬间消失”,浏览器根本没机会触发 transitionend。
常见错误现象:
- 加了
transition: opacity 0.2s,但新增项直接显示,无淡入 - 删除项时直接消失,没淡出,控制台也收不到
transitionend事件 - 误以为给
ul加transition就能带动子项动画——无效
正确做法:用显隐类 + transition + 手动生命周期钩子
核心是绕过 DOM 增删,改为「先加节点 → 再加入场类 → 动画完成后清理」和「先加离场类 → 等动画结束 → 再删节点」。
实操建议:
- 每个
li必须有唯一key(如:key="item.id"),否则 Vue/React 无法精准定位要动画的项 - 基础样式写死
transition:.list-item { opacity: 0; transform: translateY(10px); transition: opacity 0.25s ease, transform 0.25s ease; } - 入场类只改值:
.list-item.entering { opacity: 1; transform: translateY(0); } - 离场类也只改值:
.list-item.leaving { opacity: 0; transform: translateY(-10px); } - 监听
transitionend时,必须校验event.propertyName === 'opacity'或'transform',避免被其他过渡干扰
Vue3 中用 <transition></transition> 组件更省事
Vue3 的 <transition></transition> 会自动在元素插入/移除时添加/移除 v-enter/v-leave 系列类名,比手写更可靠。
关键点:
- 必须把
<transition></transition>包在循环内部,而不是包整个<ul></ul>:<li v-for="..."><transition><div>...</div></transition></li> - 不要依赖
v-enter-active写transition—— 它只在进入阶段生效,离场时失效;应统一写在v-enter-from和v-leave-to的基础类上 - 若需 stagger(逐项延迟),得配合
:css="false"+@before-enter钩子手动设style.transitionDelay
真正该用视图过渡 API 的场景
如果目标是整个列表区域的切换(比如路由跳转、tab 切换导致整块列表替换),document.startViewTransition() 是目前最干净的方案——它不关心单个 li,而是捕获 DOM 快照做 GPU 加速过渡。
但注意:
- 目前仅 Chromium 111+ 原生支持,Safari 和 Firefox 仍需 polyfill 或降级处理
- 它不适用于「动态增删个别项」,只适合「整块内容替换」
- 命名过渡(
view-transition-name)必须加在具体元素上,不能只加在ul上,否则动画锚点丢失
最易被忽略的一点:无论用哪种方式,只要涉及高度变化(比如展开/收起),就别碰 height 过渡——它强制重排。改用 max-height + 固定像素值,或直接用 transform: scaleY() 模拟。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











