十万条数据,后端不分页咋办!(如何优化长列表渲染)

长列表是什么?

我们通常把一组数量级很大的数据叫做长列表,比如渲染一组上千条的数据,我们以数组的形式拿到这些信息,然后遍历渲染在页面上;

长列表应该如何渲染?

面对这种大量的数据我们通常会采取分页拉取的形式来优化用户体验,比如直截了当的分页器,或者无限滚动,再配合懒加载等方式,这样能够满足大部分长列表的场景

但是当我们面对的数据量巨大且无法分页拉取时,上面说的方法就不好用了;在一个移动端的页面上呈现成千上万个dom,除了请求时的挑战,渲染时对于用户的设备性能也是一个极大的考验。

我尝试在一个页面中渲染近万条数据

image2022-6-22_23-2-24.png

最直观可以看到的是,在真实列表中产生了十万多个节点。虽然我们创建DOM这件事本身很简单,但是每个dom都会增加页面在内存、布局、样式、绘制方面额外的成本。如果网页的dom过多会导致低端设备变得卡顿甚至无法使用。(Admittedly, DOM nodes themselves are cheap, but they are not free, as each of them adds extra cost in memory, layout, style and paint. Low-end devices will get noticeably slower if not completely unusable if the website has too big of a DOM to manage.)

在这里尝试一种虚拟列表的渲染形式,明显的可以看到,在页面中只产生了几百个dom节点,大大提升了页面的渲染性能

image2022-6-22_22-49-35.png

虚拟列表的实现原理

虚拟列表实际上就是一种按需渲染的操作。我们在渲染上万条数据时,只需要渲染可视区当中的元素,当页面发生滚动时,监听元素并进行替换,从而达到在上万上亿条数据面前,被页面渲染的也只有不过几百个节点,达到优化长列表大数据渲染的目的。

video-9590882-a1a4e488970013d141305425d41e22ef.gif

如何实现一个虚拟列表,实际上就是在首屏加载的时候,只加载 可视区域 内需要的元素,当页面滚动时,再动态计算需要被渲染的元素,删除掉消失在视窗中的元素,保持总数一致。

<div class="mod-wraper" ref="seatListRef" > 
	<!-- 占位div 用于模拟真实列表的滚动 --> 
	<div class="mod-phantom" :style="{ height: seatListHeight + 'px' }"></div> 
	<div class="mod-realList" :style="{ transform: `translate3d(0,${startOffset}px,0)` }">
		<div class="mod-vip-seat__item" v-for="item in visibleData" :key="`${item.Index}`">
			<Item :item="item" /> 
		</div>
	</div> 
</div>

前面我们看到在虚拟列表渲染中,只渲染了可视区的数据,所以为了让页面能够正常的滚动,我们需要一个占位的元素,在上面代码的DOM结构中mod-wraper充当可视区的角色,mod-phantom是我们的占位元素,用来形成滚动条,mod-realList才是我们真正渲染出来的元素列表。我们需要定义一些变量来计算该出现在视窗中的数据。

// 假定需要渲染的数据为 listData[] , 每个元素的高度为 itemHight, 占位Dom的滚动距离为scrollTop; 
const wrapperHight = X; //视窗高度 
const seatListHeight = list.length * itemHight; //占位Dom高度 
const visibleCount = Math.ceil(wrapperHight / itemHeight); // 可视元素数量 
const startIndex = Math.floor(scrollTop / itemHeight); // 起始元素 
const endIndex = startIndex + visibleCount; // 终止元素 
const startOffset = scrollTop; //列表偏移量 
const visibleData = listData.slice(startIndex, endIndex); //可视区域数据

我们可以利用视窗的高度计算出此时视窗中的开始元素与结束元素,对listData进行裁剪。监听mod-phantom的滚动条数据对mod-realList进行translate,使页面看起来实现了真正的滚动。

video-9591726-ce0608fc3518fab47c21eb823e85779c.gif

真实的使用案例当中,我们不会只是渲染一个列表,还会存在其他元素以及宽高的不缺定性,那我们就需要设置更多的变量去进行计算。比如页面中的头部和底部数据,封装好的组件不一定能满足我们的需求,所以只有掌握了才能更好的应用于开发当中。

优化思路

  1. 由于虚拟列表是实时生成dom,所以有一定回流和重绘的成本,并且由于我们用监听滚动条来实现‘假装滚动’,Rander进程无法及时更新视图,所以在用户滑动过快时会产生页面上只剩背景的问题,我们可以通过提前渲染一些元素来缓解这个问题,比如提前渲染100条,这样用户滑得再快也不会看到白屏了。
  2. 说到了提前渲染,我们也可以使用这种方式去动态监听不定宽高的列表场景。

适用场景

说了这么多感觉虚拟列表是不是很厉害,其实通过实践发现,在几百条数据的情况下,虚拟列表与真实Dom的渲染速率毫无差别。所以我们这里所说的长列表,是几千几万条数据的情况下。

参考文档:

【1】https://developer.chrome.com/blog/infinite-scroller/

【2】https://developer.mozilla.org/zh-CN/docs/Web/API/IntersectionObserver

我的博客即将同步至腾讯云开发者社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan?invite_code=27miz13534bo8

本站文章资源均来源自网络,除非特别声明,否则均不代表站方观点,并仅供查阅,不作为任何参考依据!
如有侵权请及时跟我们联系,本站将及时删除!
如遇版权问题,请查看 本站版权声明
THE END
分享
二维码
海报
十万条数据,后端不分页咋办!(如何优化长列表渲染)
我们通常把一组数量级很大的数据叫做长列表,比如渲染一组上千条的数据,我们以数组的形式拿到这些信息,然后遍历渲染在页面上;
<<上一篇
下一篇>>