前端长列表如何流畅高效渲染

思路

长列表渲染节点数由于较多,直接渲染肯定会造成页面的卡顿,一种方式是滚动加载(infinite scrool),并非一次性渲染全部列表,而在滚动到页面底部的时候,再去加载剩余的数据,二是通过合理的逻辑来限制仅渲染可视区域部分;

当然方式一需要产品设计允许,毕竟滚动到底步才加载的方式对体验有一定折损,而方式二就需要比较复杂的逻辑了。

对于方式二实现的一些思路:

  • 首先是优化列表项,每个列表项内的节点数尽量少;
  • 不在页面显示区域的项目需要将列表高度撑起,不用渲染,只需要一个占位元素,高度为全部列表项加载完的scrollHeight

    令列表中的每个元素的高度是 itemHeight, 列表高度是 clientHeight,列表一共10000条数据,总高度为 itemheight * 10000,可视区域列表项数量为 visibleCount = Math.ceil(clientHeight / itemHeight).

  • 计算当前在可视区域的元素数据索引并渲染

    通过onScroll监听当前滚动位置 scrollTop,计算得到当前位置列表位置序号var start = Math.floor(scrollTop / itemHeight)var end = start + visibleCount, start 和 end 分别记录可见区域开始和结束位置索引;然后截取数据 data.slice(start, end)} 渲染到列表上,这样每次仅渲染 visibleCount 数量节点,大大提高渲染效率

  • 此时滚动起来后面的数据就看不见了,还需要优化滚动位置

    给占位元素加上一个 transform = `translate(0, ${fixedScrollTop}px),其中 fixedScrollTop = scrollTop - scrollTop % itemHeight.

此方法在某些scroll事件支持不好的浏览器下可能存在问题,另外还有限制列表项高度是固定的

具体实现可查看demo

此外,还思考在快递滚动下是否可以再进一步优化性能,或许可能影响一点点视觉效果,比如:

  • 在快速滚动时,跳过中间滚动列表项的细节,只需渲染出大致的骨骼框架,减少需要渲染的节点数;
  • 通过判断滚动速度低于一定阈值时就需要渲染出可视区域的列表项的完整细节了;