前端性能优化实战总结与关键技巧解析
性能优化又翻车了,这次是列表渲染太慢
最近在做一个商城项目,首页的商品列表数据量有点大,大概三四百条的样子。一开始觉得这数据量不大啊,结果真机测试的时候发现页面卡得不行,尤其是低端安卓机上,滚动起来跟幻灯片似的。
这里我踩了个坑,一开始以为是接口请求的问题,折腾了半天去优化后端接口,甚至还加了缓存。后来用Chrome DevTools的Performance面板一测,发现问题出在前端渲染上。
三种方案对比,我选了最简单的
刚开始我试了几个方法,比如直接用CSS的will-change属性来优化滚动:
.list-item {
will-change: transform;
}
这个方法亲测有效,但效果不够明显。尤其在低端机上,还是会有明显的卡顿。
接着我又试了虚拟列表,就是只渲染可视区域的内容。这个方案确实能解决问题,但实现起来比较复杂,需要计算每个item的高度,还要监听滚动事件动态更新。对于一个赶工期的项目来说,实在没那么多时间折腾。
最后我选择了Intersection Observer API来做懒加载,简单粗暴又好用。虽然不是最优解,但对于当前项目来说够用了。
核心代码就这几行
下面直接上代码,这是我最终的实现方案:
// 商品列表组件
export default {
data() {
return {
items: [], // 所有商品数据
visibleItems: [] // 当前可见的商品数据
}
},
mounted() {
this.loadItems()
this.initObserver()
},
methods: {
async loadItems() {
const res = await fetch('https://jztheme.com/api/products')
this.items = await res.json()
this.visibleItems = this.items.slice(0, 20) // 先加载前20条
},
initObserver() {
const observer = new IntersectionObserver(entries => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const index = parseInt(entry.target.dataset.index)
this.loadMore(index)
observer.unobserve(entry.target)
}
})
}, { threshold: 0.5 })
this.$refs.sentinel && observer.observe(this.$refs.sentinel)
},
loadMore(index) {
const newItems = this.items.slice(0, index + 30) // 每次多加载30条
this.visibleItems = newItems
}
}
}
<template>
<div>
<div v-for="(item, index) in visibleItems" :key="item.id">
<ProductItem :data="item" />
</div>
<div ref="sentinel" style="height: 1px;"></div>
</div>
</template>
这里有几个点需要注意:
- sentinel元素:这是用来标记观察位置的,放在列表底部
- threshold参数:设置为0.5表示当元素一半进入视口时就开始加载
- 分批加载:每次加载30条,这个数量可以根据实际情况调整
踩坑提醒:这三点一定注意
在实现过程中我遇到了几个坑,分享给大家避雷:
- 不要在Intersection Observer回调里直接修改DOM,最好通过Vue的数据驱动方式更新
- 记得在组件销毁时调用
observer.disconnect(),否则会造成内存泄漏 - 如果列表项高度不固定,需要使用
rootMargin参数预留更多观察空间
还有一个小问题没解决,当快速滚动时偶尔会出现白屏,不过持续时间很短,基本不影响用户体验,暂时就这样了。
为啥不用虚拟列表?说说我的想法
其实虚拟列表确实是最佳方案,像react-window这些库都很成熟。但我觉得对这个项目来说有点杀鸡用牛刀了。
主要原因有几点:
- 项目工期紧,老板天天催着上线
- 数据量虽然大,但还没到必须用虚拟列表的程度
- Intersection Observer兼容性已经很不错了,主流浏览器都支持
当然,如果你的项目数据量特别大,或者对性能要求特别高,建议还是老老实实用虚拟列表。
以上是我踩坑后的总结,希望对你有帮助
这次性能优化的经历让我明白,有时候最简单的方案反而最适合当前的场景。虽然最终方案不是理论上最优的,但综合考虑开发成本和实际效果,我觉得这个选择是对的。
如果你有更好的实现方式,或者对这个方案有什么改进建议,欢迎在评论区交流。后续我还会继续分享一些实战中的性能优化经验,敬请期待。

暂无评论