面试中遇到这样一个问题:给你10万条数据,一次性加载完所有数据,注意是一次性(加载的数据在页面可见,不能使用懒加载、不能用虚拟列表)面试官说可以用宏任务和微任务实现,即不影响渲染,也不会阻塞我们的js执行
阅读了这篇文章:【「前端进阶」高性能渲染十万条数据(时间分片)】 ,文章循序渐进地讲述了如何一步步地进行优化渲染,最后用到了requestAnimationFrame,然后顺便看了一下关于requestAnimationFrame的文章,收获如下:
- 假如在回调中修改dom的属性,由于每次屏幕绘制时,都会执行回调,所以修改dom属性后,修改后的属性可以立即反馈在页面上
- 假如说用setTimeout来执行动画,如果修改dom属性的间隔 < 1000/60ms,会存在属性修改后,并不会绘制的情况,这种情况下会丢帧;如果修改dom属性的间隔 > 1000/60ms,那么这个时间超过人眼视觉停留时间(姑且这么解释),效果就是会觉得卡顿、不流畅
- 渲染线程与js执行线程互斥的,假如主线程的js执行时间过长,会影响到绘制,验证方式为如下代码:
// 记录任务开始时间
let now = Date.now();
// 插入一万条数据
const total = 10000;
let excuteCount = 0;
// 获取容器
let ul = document.getElementById('container');
const once = 20;
loop(total, 0);
function loop (curTotal, curIndex) {
if (curTotal <= 0) {
console.log('总运行时间:', Date.now() - now)
return;
}
let pageCount = Math.min(curTotal, once);
window.requestAnimationFrame(() => {
let fragment = document.createDocumentFragment();
console.log('执行啦*******:', excuteCount++);
for (let i = 0; i < pageCount; i++) {
let li = document.createElement('li');
li.innerHTML = ~~(Math.random(0, 1) * total) + ':' + curIndex;
getComputedStyle(li) // 12369
ul.appendChild(li); // 11156
// fragment.appendChild(li);
}
// ul.appendChild(fragment) // 11728
loop(curTotal - pageCount, curIndex + pageCount);
})
}
// *号位置代码
for (let i = 0; i < 100000; i++) {
console.log('执行js:', Date.now() - now)
}
// 在以上代码执行过程中,*位置的代码执行完成之后,才会执行window.requestAnimationFrame内的回调函数代码