排查长列表内存溢出的问题
搞个性能优化搞了两天,一直以为是一次性创建令浏览器崩溃的dom的数量,导致浏览器内存溢出,从而卡顿,原来不是。
先来看看成品页面:
这里服务端返回了headers和data两个list数据,那么双重for。我拿到数据之后,要去往VUE的dom上绑定一个计算data.item.dates.item, 和对应的headers里item.date日期的数据,进行对有状态单元格的渲染。
实际上是三层循环。我本以为是循环过多,导致计算卡顿,于是我优化了我的一些算法,子父之间的通信取消所有大数据的传递,将近花费我半天时间,但效果并不明显。
并且我发现,打开程序前是300M内存,但是网络请求也没有返回,等到整个程序跑完,大概花了2G的内存。
起初以为是网络接口慢,于是我就跑到别的页面调用了请求,发现并不是网络的原因。而是js单线程阻塞。
我能想到的阻塞,就是js层面和dom层面了。
于是我把v-for 里面的v-for注释了,果然很快。
至此我还以为是数据渲染了dom太多。
这时候我又开始了走向偏路。
我开始询问gpt。
它建议让我用可视区展示列表。即使dom有10000个,我们也可以定义只展示dom的[1-10]个,鼠标滚动再展示接下来的[10-20]个,保证永远只有10个div(视觉欺骗)。
就避免了一次性创建10000个div的操作。很好(听说ios虚拟列表就是这么做的)。
可是vue-virtual-scroller真的很难用,页面的css要求吻合度很高才行。
这时再次找寻相关文章,想到是不是可以写懒加载,进行滚动条监听到底部,进行加载dom。。
结果发现,我这样子的列表就算局部滚动不好实现,而且。最主要懒加载之后依然会把内存逐步加到2G。
难道真的是dom到了一定数量就要内存溢出吗?
于是我去看了一下竞品的页面元素。
它的dom也是一次性渲染出来的。
为什么它可以呢?
我终于思考,因为服务端请求数据包括我对数据的处理都不至于卡死页面,主要卡死还是在内层v-for里,于是我开始寻求延时器,前端假分页累加去渲染整个页面,乞求优化一点性能。
答案依然是不好使,其实数据量不是三万条以上,一次渲染三万条以下的dom对现代浏览器是毫无压力的。那么,留着吧,万一以后数据量大了,还是有用的。
于是乎我开始了真正排查内层循环dom层面上的问题。
我开始了逐一注释,并相应关注浏览器进程的内存运行状态。
终于让我找到了卡顿的部分,就是这个正则!
我马上意识到正则很吃性能,于是乎我就换了个写法。
内存立马下来了,绑定在每个单元格上的点击事件也不再延迟了。
主要是内存不爆到2G,就不会有内存泄露的问题。就不会被垃圾回收所忽略掉。
而我前面实现的,分批次加载dom,也是为后期一次性加载3万条dom做准备。
唯一遗憾的是类似ios的长列表优化,很难做到。实在是对需求页面的css要求吻合度很高才行,并且,你也不能保证样式下次不会更改了,总是,这还是适合手机端的优化吧。