虚拟列表技术引入 - 综述

991 阅读4分钟

起因

初期,业务中播放页面事件流列表数据数量控制在 400 条以内,采用普通列表展示没有出现性能问题。

接入App 端会话后,出现事件流 5000+ 列表项的情况,渲染会消耗大量时间,同时事件流在会话播放过程中会与播放器进行频繁的互动,包括筛选、展开详情面板、手动滚动、自动滚动和实时定位等,进一步导致频繁发生重新渲染,5000+ 列表项同时改变对浏览器来说是巨大的消耗,出现卡顿情况。因此,普通列表情况下,难以满足业务需求。

采用

通过实验,最简单的定高列表加入 10000 条记录,JS 的运行时间为 38ms 左右,但渲染完成后的总时间为 957ms 左右,大部分时间都花在渲染上,同时 10000 条记录在定位和移动过程中全部元素都要重新渲染。可想而知当列表项数过多且结构复杂时,同时渲染和互动会是很大的消耗。

而虚拟列表就是解决这一问题的一种实现。

虚拟列表

虚拟列表其实是按需显示的一种实现,即只对可见区域进行渲染,对非可见区域中的数据不渲染或部分渲染的技术,从而达到极高的渲染性能,基本结构如图所示。

虚拟列表技术原理很简单,通过极少的渲染消耗达到普通列表的展示效果,但是因为本身也不是完美的,需要在特定环境下才能发挥最大优势。

虚拟列表缺陷简单来说如下:

  1. 完全依赖于列表项高度,因此在动态高度情况下会有更高的消耗
  2. 完全依赖于计算,因此对 CPU 的消耗会更大(可通过缓存,或现代 JS API,ResizeObserver 等进行 优化)
  3. 完全依赖于滚动,但 JS 处理滚动事件效率并不高(可以通过 InterSectionObserver 优化)

虚拟列表在使用的过程中还有更多需要注意的点,如果不善加利用,它不会成为解决问题的方案,反而会成为拖累。

虚拟列表只是一个概念,为完成不同场景下的渲染,比如动态高度列表或树形列表等等,需要动态的改变形态,同时也需要倍加小心,通常来说不建议小列表采用。

关于虚拟列表技术更多内容,请参考下面两篇文章:

再谈前端虚拟列表的实现

「前端进阶」高性能渲染十万条数据(虚拟列表)

类库筛选

通常来说,虚拟列表技术建议自己开发,可获得最大的灵活性,来适配业务。比如我们的 fileTree 即是自研虚拟列表的变种(无法采用通用实现方案)。

但是,如果是典型的列表场景,更好的方式是采用已经成熟的方案

事件流的场景分析,其特点为:

  • 列表项数量多
  • 列表项高度动态
  • 滚动频繁
  • 涉及筛选

是一个典型的动态高度虚拟列表场景

播放端项目技术栈分析,已采用:

  • 项目面向浏览器最低版本为 IE11 ,大部分现代 JS API 可用
  • 已经引入了完整的可靠的 InterSectionObserver polyfill,兼容浏览器版本最低可达 IE6
  • ResizeObserver 因为市面上的 polyfill 程度都有限,因此没有引入

基于 Vue 和我们的业务环境,综合以上选项,最终定位到 vue-virtual-scroller,在没有特殊业务需求的情况下,非常契合我们的场景。

落地

对事件流进行改造,因时间问题暂时直接改成虚拟列表方案,后续会根据实际情况进行动态选择(普通列表 or 虚拟列表),因此下一版本工作包含:

  • 普通列表重新开发,适配新数据结构
  • 在不同网络、机器配置和多个会话情况下进行实际测试,确定切换最佳数量节点
  • 完整版事件流列表落地