如何渲染几万条数据并不卡住界面
渲染几万条数据不卡就不能将数据一次性的渲染出来,而是需要分段渲染,一次渲染20条,30条,50条等,来分多次渲染. 先把代码贴出来然后再一一解释:
<ol></ol>
setTimeout(()=>{
// Ⅰ定义变量
let totalData = 10000;
let once = 20;
let renderCount = 0;
let loopCount = Math.ceil(totalData / once);
let ol = document.querySelector('ol');
// Ⅱ渲染
function renderData(){
let fragment = document.createDocumentFragment();
for(let i=0;i<once;i++){
let li = document.createElement('li');
li.innerHTML=Math.floor(Math.random()*totalData);
fragment.appendChild(li);
}
ol.appendChild(fragment);
renderCount+=1;
loop();
}
// Ⅲ循环
function loop(){
if(loopCount<renderCount){
window.requestAnimationFrame(renderData);
}
}
// Ⅳ开始渲染
loop();
},0)
这一串代码由setTimeout()
函数包裹,setTimeout
是一个计时器,在此函数内的代码是宏任务,需要在主线任务和微任务(不是宏任务所含微任务)执行完后再执行,这在一定程度上提高了友好度,不需要等待数据渲染完之后才继续渲染后续的界面,而setTimeout
延时时间设置为0就是为了让主页面渲染完后第一个渲染数据.
Ⅰ定义变量
在这部分内对变量进行一些定义,在此需要获取到待渲染数据的总数,一次性渲染数据条数,需要渲染的次数以及循环次数,当然还有少不了的Dom节点,这些数据可以根据自己需要进行更改.
Ⅱ渲染
在渲染这部分中定义了一个渲染函数,在此渲染函数内使用createDocumentFragment()
方法创建了一个虚拟的节点对象fragment
,将渲染的li
节点加入到fragment
这个虚拟的节点对象中,在我看来,这个虚拟对象可以看作一个容器,我们可以把需要渲染的部分数据先放到容器中,然后在将这个容器放入到对应的父节点中,在该例子中父节点是ol
,这样做的好处是防止每一次li
生成后就立即插入到ol
中,使浏览器进行重绘,而在数据较多的情况下浏览器的页面的重绘会大大的损耗浏览器的性能.而先将li
放入fragment
这个虚拟节点再将fragment
放入ol
可以减少20倍(在本例子中是20倍)的重绘次数,提高执行效率;
同时,在渲染函数内也调用了一下循环函数来判断是否结束渲染.
Ⅲ循环
在循环这部分同样定义了一个函数来进行循环判断操作,当renderCount
的次数大于loopCount
时停止渲染,
window.requestAnimationFrame()
要求浏览器在下次重绘之前调用指定的回调函数更新动画,具体用法可去往window.requestAnimationFrame - Web API 接口参考 | MDN (mozilla.org)
Ⅳ开始渲染
在此调用一次loop
函数开始数据的渲染.
总结
这个问题考察了任务队列,createDocumentFragment()
以及window.requestAnimationFrame()
这两个方法,同时还用到了重绘的知识,开阔了思路.