大数据表格性能优化-虚拟滚动

·  阅读 4744

一、场景

在我们的开发中table组件可以说是重中之重,非常频繁的使用到。但是如果碰到数据量多的时候,这个组件的性能非常堪忧。

列表10条数据的渲染时间如下

100条数据的渲染时间如下

990条数据的渲染时间如下

总结:由此可见, 这个table组件只适合渲染小数据量的,或者要进行合理的分页。

二、优化思路

(1)表格数据使用Object.freeze(data)处理,因为一般来说表格中的数据是不会进行更改的。一般进行更改后都是重新调用接口来重刷一遍数据。这样,vue不会做getter和setter的转换,即这个数据不是响应式的了,可以提高表格渲染的性能。

(2)减少使用计算属性和dom的判断渲染。有时候后端只会传给你一个状态码, 你会通过不同的状态码来渲染不同的东西, 比如{status: 0}, 这时候你将要渲染会员这个中文。这时候 你可以在js中先将数据进行转换下,变成{status: '会员'},直接渲染上去

(3)以上的方法只能解决部分问题,最后还引入vex-table组件进行根本优化

三、vex-table使用

文档连接:xuliangzhan.github.io/vxe-table/#…

*注

使用vex-table后列表990条数据的渲染时间如下

四、虚拟滚动原理

1.虚拟滚动(Virtual Scrolling):复用DOM元素,隐藏不在视图内的其它元素,使用占位符来延迟刷新数据。

从上面的结果发现,当用鼠标滚动的时候,

(1)尽管数据量是上万条,但是HTML标签元素永远就那么几个

(2)有些HTML元素会被更新,而有些HTML元素不会变化

虚拟滚动能够很好优化上万条数据的呈现跟的上用户滚动操作的速度,犹如动画般的交互顺滑。

2.Virtual scrolling - Props Design(属性设计)

(1)viewport:这里看成是Table数据的可视区域,需要提供可视区域的高度,用于计算实际渲染在DOM中Item的数量。

(2)size:每条数据在DOM中占用的高度,用于统计虚拟列表的高度,默认每行的高度为40px。

(3)Render Items:真正曾现在用户视觉上的items。

(4)Remain Items:(向上/向下)可是区域之外的留闲数据高度,不显示在viewport中,但是存在DOM中,其用于当用户滚动的距离不是很大的时候,UI不需要重新去渲染,为滚动做了一层留长优化。

接下来讲一下虚拟滚动特征

Virtual scrolling - Virtual Row Render(虚拟行渲染) 在已知viewport有高度情况下,我们可以先把每条数据看作是每一个独立的行数据,用索引来标记每条数据,用Map对象封装这些块数据,存在浏览器的内存中,当滚动事件被触发的时候,我们只需要渲染能够映射在viewport中对应的块数据,而不是遍历渲染所有块,能有效的减少HTML file size。

key = index,我们用数据索引和每条数据的高度来作为虚拟块Map的Key,结合滚动的距离和viewport的高度,来标记实际要渲染在DOM中的items,即 另外每个Item会根据Key值来定义其显示范围,如item1的key为0,则它的显示范围应该是[0~40)这个区间,而item2区间为[40,80),以此类推……其目的是用于计算item的显示区间是否在滚动的范围内。

4.Virtual scrolling - 数据移动设计 场景:当我们的列表有上万条数据,我们给定

每条数据的高度为40px,即itemHeight = 40px,

留闲高度为80,即remainHeight = 80px,

而表格的可视区域高度为80px,即viewportHeight = 80px,

由此我们可以设计存在DOM中的items最多可以渲染6条。

所以当我们在滚动的时候,当滚动的距离到item12的高度的时候,此时我们希望可视区域的数据会被刷新,并且DOM元素会被跟新如下

但是,当用户滚动的距离不是很大的话,例如它从item1滚动到item4的时候,我们希望DOM不需要被更新,即

接着我们来细节化模拟数据滚动,如下图

实线,例如item1 ~ item6,代表已经在DOM中存在的数据

黑色实体,例如item1~item2,显示在可视区域的数据,

反之灰色实体item3~item6的被隐藏了起来

而虚拟列表的数据Size决定了可视区域滚动条的大小

滚动公式设计,主要是如何确定Dom items的高度范围,即

向下滚动场景模拟: 当我们滚动的距离 小于 向上的remainHeight(80px)的时候,

其minDomItemHeight = 0,

maxDomItemHeight = viewportHeight + 2 * remainHeight(向上/向下留闲) = 240px;

但DOM中的Items为 [item1,item2,item3,item4,item5,item6] -> 没有变化

当我们向下滚动到100px的时候,

DOM中的Items为 [item1,item2,item3,item4,item5,item6,item7] -> 新增了一个item7

当我们滚动到120px的时候,

DOM中的Items为 [item2,item3,item4,item5,item6,item7] -> item1DOM元素被移除了。

所以按照用户滚动的趋势,我们统计了滚动距离时,我们的数据渲染情况如下

即滚动公式

向下取整 minItemHeight = scrollTop > remainHeight ? Math.floor((scrollTop - remainHeight)/ itemHeight) * itemHeight : 0;

向上取整 maxItemHeight = scrollTop > remainHeight ? (Math.ceil((scrollTop + viewPortHeight + remainHeight) / itemHeight) )* itemHeight : defaultRenderItemsHeight;

而defaultRenderItemsHeight需要跟viewport的高度和留长高度,来决定渲染在DOM中item的数量.

const renderItems = Math.ceil(viewPortHeight / itemHeight) + 2 * remainItems; const renderItemsHeight = renderItems * itemHeight;

5.Virtual scrolling - CSS3 transform优化数据移动

当数据往上/下滚动的时候,我们需要复用和移动DOM中的元素,使其能根据我们算出的高度显示在viewport中,一般情况会使用position:relative + top来进行元素垂直方向的偏移,但是我们知道当采用top属性,它会使UI Reflow,性能不是很好,所以我们采用CSS3中的transform变形属性来移动,其优点是不会是UI重新的Reflow和Repaint.

transform - translateY的特点如下,

(1)范围:适用所有的HTML标签元素

(2)它是指Y轴(垂直轴)方向的移动,单位可以是px,em或百分比等

(3)当y为正时,表示元素在垂直方向向下移动;

当y为负时,表示元素在垂直方向向上移动,跟我们的数学坐标系不同

性能优化:元素移动,不会引起Reflow和Repaint

6.Virtual scrolling - 列表渲染优化

我们来比较下数组更新的两种方式:变异方法和替换数组如下

即变异方式和替换数组方式中的索引值替换,会复用需要更改的DOM元素,

而数组全新赋值方式则会复用渲染整个列表(DOM元素移位),

当滚动元素的时候,数组全新赋值节点渲染情况如下,

而局部跟新节点的渲染取下,

所以当我们用translateY属性来进行元素位置移动的时候,

即使元素插入DOM的位置不是按顺序排列的,但是translateY能确保其元素它在垂直方向的距离如下图,

红色区域代表当鼠标往下滚动的时候,需要跟新的DOM元素

黄色区域则代表不需要重新渲染

所以针对列表的优化渲染,建议不要对数组全新赋值,可以考虑用数组替换 + 数组变异的方式来复用已有的节点。如果前后数组变化完全不相等的话,可以直接使用数组全新赋值方式

分类:
前端
标签:
收藏成功!
已添加到「」, 点击更改