起因
初期,业务中播放页面事件流列表数据数量控制在 400 条以内,采用普通列表展示没有出现性能问题。
接入App 端会话后,出现事件流 5000+ 列表项的情况,渲染会消耗大量时间,同时事件流在会话播放过程中会与播放器进行频繁的互动,包括筛选、展开详情面板、手动滚动、自动滚动和实时定位等,进一步导致频繁发生重新渲染,5000+ 列表项同时改变对浏览器来说是巨大的消耗,出现卡顿情况。因此,普通列表情况下,难以满足业务需求。
采用
通过实验,最简单的定高列表加入 10000 条记录,JS 的运行时间为 38ms 左右,但渲染完成后的总时间为 957ms 左右,大部分时间都花在渲染上,同时 10000 条记录在定位和移动过程中全部元素都要重新渲染。可想而知当列表项数过多且结构复杂时,同时渲染和互动会是很大的消耗。
而虚拟列表就是解决这一问题的一种实现。
虚拟列表
虚拟列表其实是按需显示的一种实现,即只对可见区域进行渲染,对非可见区域中的数据不渲染或部分渲染的技术,从而达到极高的渲染性能,基本结构如图所示。
虚拟列表技术原理很简单,通过极少的渲染消耗达到普通列表的展示效果,但是因为本身也不是完美的,需要在特定环境下才能发挥最大优势。
虚拟列表缺陷简单来说如下:
- 完全依赖于列表项高度,因此在动态高度情况下会有更高的消耗
- 完全依赖于计算,因此对 CPU 的消耗会更大(可通过缓存,或现代 JS API,ResizeObserver 等进行 优化)
- 完全依赖于滚动,但 JS 处理滚动事件效率并不高(可以通过 InterSectionObserver 优化)
虚拟列表在使用的过程中还有更多需要注意的点,如果不善加利用,它不会成为解决问题的方案,反而会成为拖累。
虚拟列表只是一个概念,为完成不同场景下的渲染,比如动态高度列表或树形列表等等,需要动态的改变形态,同时也需要倍加小心,通常来说不建议小列表采用。
关于虚拟列表技术更多内容,请参考下面两篇文章:
类库筛选
通常来说,虚拟列表技术建议自己开发,可获得最大的灵活性,来适配业务。比如我们的 fileTree 即是自研虚拟列表的变种(无法采用通用实现方案)。
但是,如果是典型的列表场景,更好的方式是采用已经成熟的方案
事件流的场景分析,其特点为:
- 列表项数量多
- 列表项高度动态
- 滚动频繁
- 涉及筛选
是一个典型的动态高度虚拟列表场景
播放端项目技术栈分析,已采用:
- 项目面向浏览器最低版本为 IE11 ,大部分现代 JS API 可用
- 已经引入了完整的可靠的 InterSectionObserver polyfill,兼容浏览器版本最低可达 IE6
- ResizeObserver 因为市面上的 polyfill 程度都有限,因此没有引入
基于 Vue 和我们的业务环境,综合以上选项,最终定位到 vue-virtual-scroller,在没有特殊业务需求的情况下,非常契合我们的场景。
落地
对事件流进行改造,因时间问题暂时直接改成虚拟列表方案,后续会根据实际情况进行动态选择(普通列表 or 虚拟列表),因此下一版本工作包含:
- 普通列表重新开发,适配新数据结构
- 在不同网络、机器配置和多个会话情况下进行实际测试,确定切换最佳数量节点
- 完整版事件流列表落地