性能问题原因分析
- 过渡动画过多:列表项数量较多时,每个列表项都应用过渡动画,会使浏览器需要处理大量动画计算,增加 CPU 和 GPU 负担。
- 频繁重排重绘:列表数据变化频繁,触发
<transition - group>
重新渲染,导致页面频繁重排重绘,影响性能。例如删除或添加列表项时,会重新计算元素位置和样式,消耗性能。
- 复杂的动画样式:如果过渡动画使用了复杂的 CSS 样式,如滤镜、变换等,会增加浏览器渲染成本,尤其是在列表项较多时,性能问题更明显。
优化方法及原理
- 减少过渡动画数量
- 原理:通过减少应用过渡动画的列表项数量,降低浏览器处理动画计算的负担。例如,只对新添加或删除的列表项应用过渡动画,而不是对所有列表项在数据变化时都应用动画。这样可以显著减少动画计算量,提高性能。
- 示例代码:
<template>
<div>
<button @click="addItem">添加</button>
<transition - group name="list" tag="ul">
<li v - for="(item, index) in list" :key="item.id" v - if="index === list.length - 1 || index === list.length - 2">
{{ item.name }}
</li>
</transition - group>
</div>
</template>
<script>
export default {
data() {
return {
list: []
};
},
methods: {
addItem() {
this.list.push({ id: Date.now(), name: '新项' });
}
}
};
</script>
<style scoped>
.list - enter - active,
.list - leave - active {
transition: all 0.5s;
}
.list - enter,
.list - leave - to {
opacity: 0;
transform: translateX(50px);
}
</style>
- 使用 CSS 硬件加速
- 原理:利用 CSS 的
will-change
属性提前告知浏览器某个元素将会发生变化,让浏览器可以提前优化,并且将动画相关的计算交给 GPU 处理,提高渲染效率。例如,对列表项的过渡动画使用 transform
属性,因为 transform
是可以触发 GPU 加速的属性,相比直接改变 left
、top
等属性,能减少重排重绘。
- 示例代码:
<template>
<div>
<transition - group name="list" tag="ul">
<li v - for="(item, index) in list" :key="item.id">
{{ item.name }}
</li>
</transition - group>
</div>
</template>
<script>
export default {
data() {
return {
list: []
};
},
methods: {
addItem() {
this.list.push({ id: Date.now(), name: '新项' });
}
}
};
</script>
<style scoped>
.list - enter - active,
.list - leave - active {
will - change: transform;
transition: transform 0.5s;
}
.list - enter,
.list - leave - to {
transform: translateX(50px);
}
</style>
- 使用节流或防抖
- 原理:当列表数据频繁变化时,使用节流或防抖可以限制
<transition - group>
重新渲染的频率。节流可以在一定时间间隔内只触发一次重新渲染,防抖则是在一定时间内如果再次触发变化,则重新计时,直到计时结束才触发重新渲染。这样可以避免过于频繁的重排重绘,提升性能。
- 示例代码(防抖):
<template>
<div>
<input v - model="inputValue" @input="debounceUpdate">
<transition - group name="list" tag="ul">
<li v - for="(item, index) in list" :key="item.id">
{{ item.name }}
</li>
</transition - group>
</div>
</template>
<script>
import { debounce } from 'lodash';
export default {
data() {
return {
inputValue: '',
list: []
};
},
methods: {
updateList() {
// 根据 inputValue 更新 list
this.list = this.inputValue.split('').map(char => ({ id: Date.now(), name: char }));
},
debounceUpdate: debounce(function() {
this.updateList();
}, 300)
}
};
</script>
<style scoped>
.list - enter - active,
.list - leave - active {
transition: all 0.5s;
}
.list - enter,
.list - leave - to {
opacity: 0;
transform: translateY(50px);
}
</style>