blog
blog copied to clipboard
列表数据的展示优化
这是一篇基于我在组内分享的总结
列表形态的数据展示是非常常见的一种数据展示方式。在大多数场景下,常见的列表展现方式是分页加载。分页加载又能分为两种:普通的分页加载和无限滚动式的分页加载。
普通的分页加载通常需要用户进行点击等行为进行列表的翻页操作,例如技术社区 Segmentfault 的 问题列表页。这种方式一般在一页数据展示完毕的底部都会显示数据分页的页码、下一页、上一页或者加载更多等按钮等待用户操作。
无限滚动式的分页加载则通常是根据用户鼠标或者触模板的向下滚动一直加载下一页的数据,直到数据加载完毕,例如 知乎首页 就是采用这种方式。这种方式的优点是当用户向上滚动时不会再去加载"上一页"的数据。
当然,有些会结合两种方式进行列表数据的展示。展示给用户的数据的前几页是无限滚动式的分页加载,当滚动到某页时,则需要通过用户的点击行为来触发下一页数据的加载,例如 思否首页 就采用了这种组合方式。
除了列表数据分页的场景之外,也有列表数据不分页的场景,数据有多少就展示多少。例如,某用户有 1000 条交易数据需要在客户端全部展示,那么,这种场景下,该怎么去优化列表数据的展示呢?
下文是我基于所负责项目上的一些方案,若有不当之处或你也有不错的思路,欢迎评论
按需渲染
按需渲染是指根据容器元素的高度以及列表项元素的高度来渲染数据。
如上图所示,假设容器元素的 height
是 500px
,而列表项元素的 height
是 50px
,那么容器元素内此时最多可以显示 10 个数据项。也就是说,当列表初始化的时候,实际上我们只需要渲染 10 个数据项即可,而不是 1000 个数据项。伪代码如下:
const demandData = originalData.slice(0, Math.ceil(containerHeight / itemHeight))
在本文中,作如下约定:originalData 表示原始数据,containerHeight 表示容器元素高度,itemHeight 表示列表项高度,demandData 表示按需显示需要的数据,scrollTop 表示容器元素的 Y 轴滚动值。
当用户发生滚动时:
当滚动位置如上图所示时,根据前面的计算,此时在容器元素内所能展示的第一个数据项应该是 3,最后一个数据项应该是 13 而不是 12,因为列表项元素 3 是只是部分隐藏了。那么,此时根据 originalData
进行对应的数据项截取即可。伪代码如下:
const from = Math.floor(scrollTop / itemHeight);
const to = Math.ceil((scrollTop + containerHeight) / itemHeight);
const demandData = originalData.slice(from, to)
这样就基本实现了列表数据的按需显示。
延迟渲染
延迟渲染是一种常见的首屏优化方案,其基本思路是应用初始化时,首屏根据客户端的 Viewport 只加载用户可见的部分,当用户滚动对应位置时,才去加载对应元素,以缩减首屏的加载时间。
延迟渲染则和延迟加载如出一辙。列表初始化时,根据容器元素的高度以及列表项元素的高度,依然只是加载列表初始化所需的数据项。伪代码如下:
const demandData = originalData.slice(0, Math.ceil(containerHeight / itemHeight))
当用户往下滚动时,再动态计算需要往列表后追加的数据项。伪代码如下:
const from = demandData.length;
const to = Math.ceil((scrollTop + containerHeight) / itemHeight);
const demandData = [].concat(demandData, originalData.slice(from, to))
与按需渲染不同的是,延迟显示不再是对 originalData
进行截取,而是对 demandData
进行追加,直到用户滚动到底部,所有数据全部显示完毕为止。
总结
按需渲染和延迟渲染仅是个人的一些思考,当然,根据上述的分析,这两个方案也是有限制的:
-
container
元素需要有固定的可视高度 - 列表项元素需要有固定的高度
而大多数分页的实现中,列表项元素的高度一般不是固定的,这给予开发者极大的自由度,固定高度反而会增加限制。
另外,基于这两个方案,个人开发了一个基于 Vue 2.x 的列表展示组件:v2-lazy-list。而数据的展示不仅限于列表,很多场景下也会使用 Table 来展示数据。从我个人来看,Table 和分页往往是一起使用的;但也有使用 Table 却不分页的场景,因而根据项目需要,便将「按需渲染」的方案也整合到了 Table 中,实现 Table 数据的按需渲染,具体可见 demo。
如果你也有不错的优化方案,欢迎评论。
看到饿了么前端团队的两篇列表优化的文章:
搜集的其它相关文章:
- https://developers.google.com/web/updates/2016/07/infinite-scroller
- http://itsze.ro/blog/2017/04/09/infinite-list-and-react.html
FlatList is the magic