js实现虚拟滚动
当我们有一个大型数据需要以列表的形式渲染到页面上的时候,由于生生了大量的dom从而导致页面性能降低,出现页面渲染慢,滚动卡动等问题。
虚拟列表做的事就是确保性能的前提下,采用虚拟滚动来进行性能优化。
传统列表渲染
这里我们模拟一个10万条的大型列表数据:
html dom结构如下:
电脑有点拉垮哈,此时风扇已经开始疯狂运转。
如果这里再增加一些交互操作,浏览器都有可能崩溃。
虚拟滚动的原理
今天我们来实现一个最简单的可视区高度固定
,单条数据高度固定
且每条数据的高度都固定
。
实现原理分为一下几步:
- 计算容器高度:计算需滚动元素所在的容器高度(指定的某个可视区域高度或浏览器窗口高度);
- 创建占位元素:占位元素的高度等于实际列表的总高度,但不包括实际数据,只用于撑开滚动条,达到滚动效果;
- 确定容器可容纳的数据条数:根据容器高度与每条数据所占高度,确定容器内同一时刻可视的数据条数;
- 监听滚动事件:当监听到滚动事件时,根据当前的滚动位置与数据高度,计算需要展示在可视区域中的数据内容;
- 渲染:将上一步中确定的当前数据内容渲染在界面的可视区域内。
初始化渲染数据
单条数据高度确定itemSize
,可视区高度确定200px
。可以用二者计算出显示条数。初始索引为0,结束索引为显示条数。之后截取总数据当中对应的数据即可。
// 可显示的列表项数
visibleCount () {
return Math.ceil(this.screenHeight / this.itemSize)
},
...
mounted () {
// 开始索引
this.start = 0
// 结束索引
this.end = this.start + this.visibleCount
},
滚动逻辑
可视区高度确定,列表需要模拟出滚动条,这里采用占位div撑开方案。随着滚动条滚动,监听滚动高度计算出开始索引和结束索引,再计算出滚动偏移量,再利用translate3d滚动,translate3d且因为没有重排重绘,所以性能更好
。
methods: {
scrollEvent () {
// 当前滚动位置
const scrollTop = this.$refs.list.scrollTop
// 此时的开始索引
this.start = Math.floor(scrollTop / this.itemSize)
// 此时的结束索引
this.end = this.start + this.visibleCount
// 此时的偏移量
this.startOffset = scrollTop - (scrollTop % this.itemSize)
}
}
再渲染逻辑
随着滚动条滚动,监听滚动高度计算出开始索引和结束索引,重置开始和结束索引,也就自然引发Vue重新渲染。
现在不同框架下均有虚拟滚动工具可用,比如:react-virtualized、vue-virtual-scroll-list、ngx-virtual-scroll等等。