最近在看列表性能优化,碰到了两个很常见的概念:无限滚动和虚拟列表。
一开始我会觉得这两个东西有点像,都是在处理“列表很多”的问题,但后来整理了一下,发现它们其实关注点不一样。
这篇就当作我自己的学习记录,先把目前的理解记下来。
做列表页面时,数据一多,页面就很容易出问题。最常见的两个问题是:
- 一次加载太多数据,首屏很慢
- 页面里渲染了太多节点,滚动越来越卡
前端里经常会用两个办法来处理:
- 无限滚动
- 虚拟列表
我现在的理解是:这两个名字看起来有点像,但它们解决的不是同一个问题,最好分开看。
什么是无限滚动
我现在会把无限滚动理解成:
用户往下滑,滑到底部时,再去请求下一批数据。
比如平时刷信息流页面,最开始只看到一部分内容,继续往下滑,又会自动加载新的内容,这就是无限滚动。
它的重点在于:不要一开始就把所有数据都拿回来,而是分批加载。
这样做的好处我觉得很明显:
- 首屏更快
- 请求压力不会一下子太大
- 用户可以边看边加载
不过我也注意到,它有一个问题:
虽然每次只加载一部分数据,但旧数据会一直留在页面里。随着内容越来越多,页面上的 DOM 节点也会越来越多,最后滚动性能还是可能变差。
所以我现在会这样记:
无限滚动主要解决的是“怎么分批拿数据”。
什么是虚拟列表
虚拟列表我现在会理解成:
数据可以很多,但页面上只真正渲染当前屏幕附近能看到的那一小部分。
比如有一万个列表项,正常做法可能会把一万个节点都渲染出来;但虚拟列表不会这样做,它只保留可视区域附近的几十个节点,其他节点先不挂到页面上。
它的重点在于:控制页面里真实渲染的节点数量。
这样做的好处我目前理解是:
- DOM 数量少很多
- 页面更流畅
- 长列表滚动时不容易卡
但虚拟列表也不是万能的。
如果你一开始就把一万条数据全部请求回来,即使页面只渲染一小部分,接口返回还是会慢,首屏依然可能白屏等待。
所以我也会这样记:
虚拟列表主要解决的是“怎么少渲染节点”。
它们最大的区别是什么
我一开始就有点把这两个概念混在一起,后来觉得最简单的区分方法就是:
- 无限滚动:关注数据怎么一批一批加载
- 虚拟列表:关注数据很多时页面怎么渲染得动
一个偏向“请求数据”,一个偏向“渲染页面”。
这就是我目前觉得最核心的区别。
为什么它们可以一起使用
如果只用无限滚动,会有一个问题:虽然数据是分批来的,但页面节点会越积越多。
如果只用虚拟列表,也有一个问题:虽然页面渲染压力小,但如果数据一次性请求太多,还是会慢。
所以我现在会觉得,很多场景里更合理的做法其实是两者配合:
- 用无限滚动来控制数据分批获取
- 用虚拟列表来控制页面渲染数量
这样就能同时做到两件事:
- 数据不用一次拿太多
- 页面也不用一次渲染太多
简单说就是:
数据来的快,页面也渲染得快。
我给自己找了个比较好记的例子
假设我去图书馆搬书。
无限滚动像什么
不是一口气把整个书架的书都搬过来,而是先搬一小批,看完再搬下一批。
这解决的是:一次搬太多太慢太累。
虚拟列表像什么
虽然仓库里有很多书,但你的书桌上只摆当前正在看的几本,其他的先放在旁边,需要时再换上来。
这解决的是:桌子太小,摆太多会乱。
这个例子对应到前端,我觉得会更容易理解:
- 搬书的过程,像数据加载
- 书桌上摆多少本,像页面渲染多少节点
我目前觉得容易混淆的地方
误区一:无限滚动就是性能优化的全部
不是。
无限滚动只是把数据分批拿回来,但页面上已经渲染出来的节点不会自动减少,所以列表很长时,还是可能卡。
误区二:虚拟列表就一定会让首屏更快
也不一定。
如果后端一次返回的数据还是很多,那么等待数据的时间不会消失。虚拟列表优化的是渲染,不是网络请求本身。
误区三:这两个方案只能二选一
不是。
很多真实项目里,本来就是一起用的。它们关注的是不同层面,组合起来反而更完整。
先记住这几句
如果以后我自己再回头看这篇,我觉得先记住下面几句话就够了:
- 无限滚动:滚动到底再加载下一批数据
- 虚拟列表:只渲染当前能看到的那部分节点
- 无限滚动解决数据分批加载问题
- 虚拟列表解决大量节点渲染问题
- 两者可以一起使用
总结
无限滚动和虚拟列表,名字看起来像一类东西,但其实各自负责的事情不同。
无限滚动更像是在管“数据怎么来”,
虚拟列表更像是在管“页面怎么画”。
如果把它们放在一起理解,这个问题就会清楚很多:
- 数据太多,不能一次全拿,用无限滚动
- 节点太多,不能一次全渲染,用虚拟列表
- 两边压力都大,就两个一起上
对我来说,现阶段先把这条主线理解清楚,比一开始就去背复杂实现细节更重要。