JS如何优雅的处理大批量数据?

mac2024-11-21  26

本文主要是针对JavaScript(以下简称JS)处理大批量数据时,产生性能问题的简要分析,以及如何优雅的演化提升性能的解决方案;

常规方式 =》【一次性渲染】:scheme1.js【定时器分批加载】:scheme2.js告知浏览器动画处理 =》【window.requestAnimationFrame】:scheme3.js文档片段化 =》【document.createDocumentFragment】:scheme4.js

场景:假设接口返回10W条数据量,前端人员需要相应业务处理并绑定渲染在html页面,如何一步一步的分析并优化 JS 的性能,提升数据处理速度;

假设我们需要处理的需求如下,从0到10W条数据通过ul>li的方式显示在html页面上:

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>JS大批量数据渲染测试Demo</title> </head> <body> <!-- <ul id="container"> <li>1</li> <li>2</li> <li>3</li> <li>...</li> <li>10000</li> </ul> --> <ul id="container"> </ul> <script type="text/javascript" src="../js/scheme1.js"> //依次修改后面演化的方案【scheme2.js】,【scheme3.js】,【scheme4.js】 </body> </html>

我们该如何让 JS 优雅的处理这10W条数据呢?

1.常规方式 =>【一次性渲染】:scheme1.js

let now = Date.now(); const total = 100000; // 插入的数据总数 let oContainer = document.getElementById('container'); // DOM:document object model 文档对象模型 for (let i = 0; i < total; i++) { let oLi = document.createElement('li'); // 创建li元素 oLi.innerHTML = i; //注意区分:innerHTML ,innerText ?? oContainer.appendChild(oLi); } console.log('js运行时间:' , Date.now() - now); // 渲染页面 setTimeout(()=>{ console.log('总的运行时间:' , Date.now() - now); }, 0);

测试浏览器:【QQ浏览器极速内核】

 测试结果:

2.【定时器分批加载】:scheme2.js

const total = 100000; let oContainer = document.getElementById('container'); const once = 2000; // 一次插入的条数 const page = total / once; // 总的页数 const index = 0; // 数据的索引 function insert (curTotal, curIndex){ if(curTotal <= 0){ return; // 递归的出口 } setTimeout(()=>{ for(let i=0; i<once; i++){ let oLi = document.createElement('li'); oLi.innerHTML = curIndex + i; oContainer.appendChild(oLi); } // 递归的入口 insert(curTotal - once, curIndex + once); //[curTotal - once]:剩余的条数,[curIndex + once]:开始的索引 }, 0); } insert(total, index); //递归调用

测试结果:

3.告知浏览器动画处理 =》【window.requestAnimationFrame】:scheme3.js

const total = 100000; let oContainer = document.getElementById('container'); const once = 2000; // 一次插入的条数 const page = total / once; // 总的页数 const index = 0; // 数据的索引 function insert (curTotal, curIndex){ if(curTotal <= 0){ return; // 递归的出口 } window.requestAnimationFrame(()=>{ for(let i=0; i<once; i++){ let oLi = document.createElement('li'); oLi.innerHTML = curIndex + i; oContainer.appendChild(oLi); } // 递归的入口 insert(curTotal - once, curIndex + once); //[curTotal - once]:剩余的条数,[curIndex + once]:开始的索引 }); } insert(total, index); // 递归调用

测试结果:

4.文档片段化 =》【document.createDocumentFragment】:scheme4.js

const total = 100000; let oContainer = document.getElementById('container'); const once = 2000; // 一次插入的条数 const page = total / once; // 总的页数 const index = 0; // 数据的索引 function insert (curTotal, curIndex){ if(curTotal <= 0){ return;// 递归的出口 } window.requestAnimationFrame(()=>{ let fragment = document.createDocumentFragment(); // 文档碎片 for(let i=0; i<once; i++){ let oLi = document.createElement('li'); oLi.innerHTML = curIndex + i; // oContainer.appendChild(oLi); fragment.appendChild(oLi); } oContainer.appendChild(fragment); // 递归的入口 insert(curTotal - once, curIndex + once); //[curTotal - once]:剩余的条数,[curIndex + once]:开始的索引 }); } insert(total, index); // 递归调用

测试结果:

备注:以上测试受浏览器(型号,版本)影响,电脑内存,硬件以及【FPS=》是图像领域中的定义,是指画面每秒传输帧数,通俗来讲就是指动画或视频的画面数】,大多数显示显示器60Hz;本人同一个浏览器每个方案测试三次,基本都有一定的时间偏差,以上只是个基本的参考。

总结:

a.浏览器是顺序解析,JS是阻塞加载,会阻塞DOM树或阻塞渲染树的构建;

b.JS中setTimeout()执行的时间并不唯一准确;

c.windows.requestAnimationFrame() =》告诉浏览器——你希望执行一个动画,并且要求浏览器在下次重绘之前调用指定的回调函数更新动画;

d.重排【Reflow】必定会引发重绘,但重绘【Repaint】不一定会引发重排;https://www.cnblogs.com/yadongliang/p/10677589.html

e.document.createDocumentFragment() =》他们是DOM节点,并不是主DOM树的一部分。通常的用例是创建文档片段,将元素附加到文档片段,然后将文档片段附加到DOM树。在DOM树中,文档片段被其所有的子元素所代替。因为文档片段存在于内存中,并不在DOM树中,所以将子元素插入到文档片段时不会引起页面回流(对元素位置和几何上的计算)。因此,使用文档片段通常会带来更好的性能。

最新回复(0)