列表过渡
<TransitionGroup> 是 Vue 专为列表设计的过渡组件,支持列表项的进入、离开和位置移动动画。
基础列表过渡
简单实现
vue
<template>
<TransitionGroup tag="ul" name="fade">
<li v-for="item in items" :key="item.id" class="list-item">
{{ item.text }}
<button @click="remove(item.id)">删除</button>
</li>
</TransitionGroup>
</template>
<script setup>
import { ref } from 'vue'
const items = ref([
{ id: 1, text: '项目 1' },
{ id: 2, text: '项目 2' },
{ id: 3, text: '项目 3' }
])
const remove = (id) => {
items.value = items.value.filter(item => item.id !== id)
}
</script>
<style>
.fade-enter-active,
.fade-leave-active {
transition: all 0.3s ease;
}
.fade-enter-from,
.fade-leave-to {
opacity: 0;
transform: translateX(-20px);
}
.fade-leave-active {
position: absolute;
}
</style>
position: absolute在leave-active状态时使元素脱离文档流,避免其他元素位置跳动。
排序动画
FLIP 自动动画
vue
<template>
<TransitionGroup tag="div" name="item" class="grid">
<div v-for="item in sortedItems" :key="item.id" class="card">
{{ item.name }} - {{ item.value }}
</div>
</TransitionGroup>
<button @click="sortBy('name')">按名称排序</button>
<button @click="sortBy('value')">按值排序</button>
</template>
<script setup>
import { ref, computed } from 'vue'
const items = ref([
{ id: 1, name: 'C', value: 30 },
{ id: 2, name: 'A', value: 10 },
{ id: 3, name: 'B', value: 20 }
])
const sortKey = ref('id')
const sortedItems = computed(() => {
return [...items.value].sort((a, b) => a[sortKey.value] - b[sortKey.value])
})
const sortBy = (key) => {
sortKey.value = key
}
</script>
<style>
.item-move {
transition: transform 0.4s ease;
}
</style>
Vue 使用 FLIP 技术自动计算新旧位置差异,无需手动编写排序动画逻辑。
添加动画
新项进入效果
vue
<template>
<TransitionGroup tag="ul" name="slide">
<li v-for="item in items" :key="item.id">
{{ item.text }}
</li>
</TransitionGroup>
<button @click="add">添加项目</button>
</template>
<script setup>
import { ref } from 'vue'
let nextId = 4
const items = ref([
{ id: 1, text: '项目 1' },
{ id: 2, text: '项目 2' },
{ id: 3, text: '项目 3' }
])
const add = () => {
items.value.push({ id: nextId++, text: `项目 ${nextId - 1}` })
}
</script>
<style>
.slide-enter-active {
transition: all 0.3s ease;
}
.slide-enter-from {
opacity: 0;
transform: translateY(-20px);
}
.slide-leave-active {
transition: all 0.3s ease;
}
.slide-leave-to {
opacity: 0;
transform: translateY(20px);
}
.slide-move {
transition: transform 0.3s ease;
}
</style>
性能优化
虚拟化列表
vue
<template>
<TransitionGroup tag="div" name="item">
<div v-for="item in visibleItems" :key="item.id" class="item">
{{ item.content }}
</div>
</TransitionGroup>
</template>
<script setup>
import { ref, computed } from 'vue'
const allItems = ref([...]) // 大量数据
const scrollTop = ref(0)
const visibleItems = computed(() => {
// 仅渲染可视区域项
return allItems.value.filter(item => isInViewport(item))
})
</script>
大数据量列表建议配合虚拟化使用,仅渲染可视区域项,避免 DOM 过多导致性能问题。
动画时长控制
vue
<TransitionGroup :duration="300" tag="ul" name="list">
<li v-for="item in items" :key="item.id">{{ item.text }}</li>
</TransitionGroup>
列表项较多时,统一设置
:duration避免动画时长推断错误。
注意事项
TransitionGroup不支持mode属性,列表项同时进入/离开- 必须为每个列表项设置唯一
key,不能使用索引 leave-active状态设置position: absolute避免布局跳动- FLIP 动画对
transform和opacity性能最佳,避免修改width/height
要点总结
TransitionGroup专为列表设计,支持进入、离开、移动三种过渡- FLIP 技术自动计算位置差异,简化排序动画实现
leave-active使用position: absolute脱离文档流- 大数据量列表建议配合虚拟化优化性能
存放路径: articles/VUE/进阶/过度动画与状态复用/列表过渡.md
📝 发现内容有误?点击此处直接编辑