业务代码中,最常使用的一类组件就是列表。在之前,当有很多列表需要展示时,会通过分页的方式来展现,通过分页能够保证高性能和适当的内存使用,但是不足之处在于,分页的切换会打断用户的浏览连贯性,同时也会增加用户的等待时间。
现在流行的长列表加载方式时,用户在即将滚动到页面底部时,就自动通过 ajax 请求下一页的内容,然后渲染在页面上,从而实现几乎无缝的浏览体验(排除网络状况不佳的情况)。但是这种方法也有缺点,那就是当用户不断浏览时,页面上的列表节点会越来越多,导致内存使用大大提升的同时,性能也跟着下降。
所以本文就来探讨一下无限滚动列表实现时的一些思路与要点。
列表渲染
要实现列表,最基础的当然就是将列表渲染出来了,通过 vue 我们只需要提供一组数据,就能够非常方便的渲染出一个列表。所以这一点在下午中不会赘述。
列表可见部分与不可见的计算与重渲染
要优化长列表的性能和内存,很容易想到的一个方向就是只对屏幕(或者列表容器)区域的可见列表进行渲染,而不可见部分的列表则不渲染。当列表滚动时,对于已经滚出屏幕外的列表项要移除,滚入屏幕内的列表项重新渲染。通过这种方式,保证 dom 中实际存在的列表项数量维持在一个低开销的状态。
同时,为了更好地用户体验,可以缓冲的列表项。如果当前屏幕只能显示 5 项,那么实际上可以将这 5 项前后的 3 项也同时渲染出来,一共渲染 11 项。这样当用户滚动时,上下各多出来的 3 项可以有效防止在新的列表还没完成渲染之前出现的部分空屏情况。
滚动区域总高度的计算
由于只渲染可见的列表项,所以需要计算出滚动区域的总高度。
假设数组中一共有 100 个列表项,每项高度都为 10,全部渲染完的总高度是 1000;单屏渲染项加上缓冲项一共有 11 项,那么初次渲染时,滚动区域高度只有 110,滚动到第 11 项底部时就无法再往下滚动。
此时,如果我们能预先知道滚动区域的总高度,就能够解决这个问题了。
还是上面这个例子,在初次渲染时就将滚动区域的总高度撑到 1000,比如在最后一个列表项下面渲染一个高度 890 的 div,这样就不存在无法滚动的问题了。而当列表向上滚动时,滚出屏幕的不可见项会移出 dom,为了防止滚动条抖动,也需要在第一个列表项上面渲染一个已经移出的总高度的 div 来撑起。
对于高度固定的列表来说,计算总高度很方便;但是实际情况中,我们往往会写动态高度的列表,列表项高度时由内容撑起的。这种情况下我们没法准确计算出总高度,所以就需要对总高度进行估算。
以上三点是作者在写这个 demo 时发现的一部分问题。另外一些问题作者暂时并没有没有去实现或无法解决,写在下面,感兴趣的小伙伴可以想想该如何解决,如果你有想法欢迎告诉我哦!
- 当容器 resize 的时候,如何实现列表的重新渲染,并且保证滚动条的位置不发生非常大的偏移?
- 当列表项中存在展开与收起时,此时每一项的高度都是活动的,如果这个活动高度也是不固定的话,该如何计算可见项与高度呢?
下面先放出 demo 的 github 地址,小伙伴们可以自己跑跑看,当然 bug 是存在的,只是暂时没有时间去更好地优化它:)
下一篇将会对 demo 的代码逻辑进行梳理讲解,不想看源码的小伙伴也可以等下一篇文章哦!
扫码关注微信公众号【前端程序员的斜杠青年进化录】 微信扫码,给我赞赏一下~
网友评论