本文主要是针对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树中,所以将子元素插入到文档片段时不会引起页面回流(对元素位置和几何上的计算)。因此,使用文档片段通常会带来更好的性能。