聊一聊 React 的 virtual scroll

2020-07-10

也没啥高见,重复一下网上现有的资料而已

 

Virtual scroll 解决了什么问题 #

Virtual scroll 是绘制超大型列表的性能解决方案。众所周知当 DOM 元素变得很多的时候,渲染元素将消耗可观的时间。即使元素是分配获取、绘制的,浏览器的内存消耗、计算元素位置的 CPU 消耗等也会上升,列表滚动及其他操作的延迟就会增加。

Virtual scroll 是怎么做的 #

正如某相关模块 react-window 的名字,你是通过一扇窗户来看这个大列表,也只有窗户里的东西才会绘制(当然可以设置余量 overscan)。

其他解决方案的问题 #

(没错,我只知道 LazyLoad)

Lazyload #

对于 React 而言,react-lazyload 中需要 lazyload 的元素呆在同一个列表中,这意味着如果一个页面有两个列表,一个的滚动将引起另一个列表内元素的重新计算。而且如果你有一个 overflow: scroll 的列表和一个随页面滚动的列表,将引起事件处理的混乱。

一般来说,LazyLoad 并没有解决 DOM 元素数量级的问题,只是把复杂的元素变得简单,到时候再绘制。如果有大规模筛选列表的操作,Unmount LazyLoad component 也是一笔不小的开支。

如果滚动到很下方,对上面的元素如何处置又是一个问题。Unmount 吧,滚动位置会突变;hide 吧,实质作用并不大,仍然参与计算,内存也占用着。

Virtual scroll 的难点及 React 现有模块简评 #

这里主要针对列表元素高度未知(dynamic,动态,与随时间变化不同),画出来才知道的情况。固定高度的可以简单搞定。

react-window 也是一个好模块,但是动态功能难产,就一笔带过了。

基本问题 #

如何确定每个元素的位置是个难题,如果要求处理 resize (滚回去元素高度会变化)就更难了。

如果你还要求滚过的元素记住状态,对不起,自己实现或者弃坑。

没有浏览器加持,布局受限 #

平常做点网页的效果只需要 CSS 什么的就可以搞定。但是这里全靠手动,还不一定能成功 🤷‍♂️

复杂算法,体积庞大 #

如果还要装作列表是随页面滚动的呢?什么?还要一起滚动的表头和表尾?表头还不能 unmount?……一堆需求下来,就有了体型庞大的 react-virtualized (35 KB min+gzipped) (虽然支持一定程度的 tree-shaking)

性能和懒难以兼得 #

本来就是为了性能才使用 virtual scroll 的(大雾

比如 react-virtuoso 虽然可以支持大小随时间变化,但是估计是加了元素大小变化事件监听,快速滚动下来明显不如 react-virtual 顺畅,甚至可以看到表尾不呆在应该有的底部。

bug 和问题频出 #

(看了 Issue 区就有点劝退了)

简单讲一个 react-virtual 碰到的问题。因为元素高度会变化,在图片加载后让 react-virtual 重新计算。但是重新计算意味着把所有元素的高度都看做是估计值。将会出现多次尝试绘制,再次触发的重新计算甚至引起绘制的死循环。同时滚动会出现跳跃。。😭

👍
1
Leave your comments and reactions on GitHub