经常看到一些网站上有这种标题:假设后端一次性给你10万条数据,前端应该怎么展示?
这种问题真的很**,骂归骂,闹归闹,在现实的业务中虽然没出现过这种一次性渲染10w条数据的情况,但是我现在做的业务确实出现了类似的场景。
网上有很多处理10w条数据的方案:
1、使用定时器分组分批分堆依次渲染
2、使用requestAnimationFrame替代定时器去做渲染
3、分页加载
4、虚拟列表
5、web worker
等....
这些方案就不要一一描述了,网上有很多例子;
现在来说下我现在业务的场景:
场景:
当前业务属于招标业务中的线路招标,用户会一次性招标多条线路,线路的数量是很多的,可能一次性会有最多三千多条明细,然后会生成几百个表格,以前用一个table2就能解决性能问题,因为你线路再多都是在一个表格中,table2完全能承载一次性上千条线路的量;现在用户想对交互进行调整,他希望相同线路编码的线路放在一个表格中,换句话说就是如果存在几百上千条线路,这些线路会进行分组,把相同类型的放在一个表格中,那么就会有几十上百个表格,如下图展示:
此时我们的页面上会有60多个表格,每个表格都有差不多50多个字段,dom元素如下:
首先我们需要确定一次性渲染这么多表格为什么会卡顿,用大白话来讲就是页面一次性渲染的节点太多了,我们每个表格中的单元格都是一个输入框或者下拉选择框,这比渲染一个文本节点是要消耗大的,加上表格多,字段多,不卡才怪。这都有几万个甚至十几万个节点了。
那么其实这也就是性能优化的本质了,我理解的性能优化就是在页面尽可能的一次性减少节点的渲染。
那么既然找到问题的本质,怎么解决这个问题呢,遇到这种问题大家首先想到的应该是使用虚拟列表了,实现虚拟列表的方式当然也有很多中,使用插件,或者手写;
这里我使用了一个js API 交叉观察器--IntersectionObserver
mounted () {
setTimeOut (() => {
const ob = new IntersectionObserver((entries) => {
for (const enter of entries) {
const target = entry.target
const index = Number(target.dataset.index)
if (entry.isIntersecting) {
console.log('出现')
this.biddingData[index].show = true
} else {
console.log('隐藏')
this.biddingData[index].show = false
}
}
}, {
threshold: 0,
rootMargin: '-50px 0px 100px 0px',
root: document.querySelectorAll('.bd-bidding-wrap')
})
const cell = document.querySelectorAll('.bidding-cell')
cell.forEach(item => {
ob.observe(item)
})
})
}
这里我在数组的每个对象中增加了一个show的属性,这个属性在后面有大用
其实这样还不能达到最大的优化效果,还需要进行一些细微的优化
所有的优化都围绕一个点进行,就是减少渲染节点,那么我们就可以在节点上下手,当表格在可视区域外的时候,我们会把表格中的所有节点都变成空节点,这样就能达到减少节点的目的,这就用到了交叉观察器中设置的show这个属性,
当表格不在可是区域中时,我们会把show这个属性设为false,当show为false的时候隐藏节点,当然你可以隐藏所有节点,但是你得给空div预留一个高度,否则滚动的时候滚动条会跳动