"Senior front-end" high-performance rendering one hundred thousand data (time slicing)


Foreword

In practice, we rarely encounter a situation you need to insert a one-time data to the hundreds of thousands of pages, but in order to enrich our knowledge, we need to understand and clearly when it comes to large amounts of data, how can without rendering data card main page, as well as the principles which behind.

The most brutal approach (one-time rendering)

<ul id = "container"> </ ul> 
Copy Code 
// Start Task time 
the let Date.now = now (); 
// data is inserted one hundred thousand 
const = Total 100000; 
// Get vessel 
let ul = document. the getElementById ( 'container'); 
// data inserted into the container 
for (the let I = 0; I <Total; I ++) { 
 the let Li = document.createElement ( 'Li'); 
 li.innerText = ~ ~ (Math.random () * total) 
 ul.appendChild (Li); 
} 
the console.log ( 'the JS run time:', Date.now () - now); 
the setTimeout (() => { 
 the console.log ( 'total run time:' , Date.now () - now); 
}, 0) 
// Print: the JS run time: 187 
// Print: total run time: 2844 
copy the code

We cycle operation of thousands of records, JS run time of 187ms, still pretty fast, but the total time after the completion of the final rendering indeed 2844ms.

Briefly explain why two huge console.log result of the time difference, and how to be a simple statistical JS run time and total render time:

  • In the Event Loop JS, when the execution stack JS engine management in the event as well as all micro-tasks all events after the implementation, rendering thread will trigger the page is rendered

  • The first console.log trigger time is the time interval before the page is rendered, obtained at this time is required to run JS

  • The second setTimeout console.log is put in, it is time to trigger the rendering is complete, the next Event Loop executed

For details on the Event Loop, see this article ->

In accordance with the results of the two console.log, it can be concluded:

For large amounts of data when rendering, JS computing bottleneck is not the performance bottleneck lies in rendering performance stage

Using the timer

From the above example, we already know that the page Caton is due at the same time rendering a large number of DOM caused, so we consider the rendering process in batches

Here, we use setTimeout to implement batch rendering

<ul id = "container"> </ ul> 
copy the code 
// need to insert a container 
the let UL = document.getElementById ( 'Container'); 
// data is inserted one hundred thousand 
the let Total = 100000; 
// first insert 20 
Once = 20 is the let; 
// total number of pages 
the let Page = total / Once 
// index for each record 
the let index = 0; 
// cyclic loading data 
function loop (curTotal, curIndex) { 
 IF (curTotal <= 0) { 
 return to false; 
 } 
 // how many page 
 the let, pageCount = Math.min (curTotal, Once); 
 the setTimeout (() => { 
 for (the let I = 0; I <, pageCount; I ++) { 
 the let Li = document.createElement ( ' Li '); 
 li.innerText curIndex + I + =': '~ ~ + (Math.random () * Total) 
 ul.appendChild (Li) 
 }
 Loop (curTotal -, pageCount, curIndex +, pageCount) 
 }, 0) 
} 
Loop (Total, index); 
duplicated code

With a look at the map gif effect

"Senior front-end" high-performance rendering one hundred thousand data (time slicing)


We can see that page load time is very fast, you can quickly see all the data of the first screen of each refresh, but when we fast scrolling pages, you will find page splash screen or white screen phenomenon

Why is there a splash screen phenomenon it

First, sort out some of the concepts. FPS indicates the number of times per second the screen update. We usually see the screen are made of a continuous web composed of still pictures, each picture is called a frame, is described in the FPS frame rate of change of the physical quantity.

Most computer monitors refresh rate is 60Hz, roughly equivalent to 60 times per second redraw, the FPS is 60frame / s, the affected screen resolution, screen size and graphics for this setpoint.

Therefore, in case you do nothing at the computer screen, most of the display will be at a frequency of 60 times per second is constantly updating the image on the screen.

Why do you feel this change?

That is because the human eye has a visual effect to stay, that is, before a picture of the impression left in the brain has not disappeared, immediately after a picture just come up, the middle interval of only 16.7ms (1000 / 60≈16.7) so will you mistake the image on the screen is stationary.

The screen gives you the feeling is right, imagine if the refresh rate becomes 1 / second, the image on the screen will flicker serious, so it is easy to cause eye fatigue, aches and dizziness and other symptoms .

Most browsers will be to redraw the operating restrictions, no more than redraw frequency of the display, because even more than the frequency of the user experience will not be improved. Thus, the most preferred smooth animation cycle interval is 1000ms / 60, is approximately equal to 16.6ms.

Intuitive feel, experience different frame rates:

  • Animation frame rate can reach 50 ~ 60 FPS will be very smooth, people feel more comfortable;

  • Animation frame rate between 30 ~ 50 FPS, because of the sensitivity of each one different comfort vary;

  • In the animation frame rate of 30 FPS or less, to make people feel obvious Caton and discomfort;

  • Great animation frame rate fluctuations, will make people feel Caton.

简单聊一下 setTimeout 和闪屏现象

  • setTimeout的执行时间并不是确定的。在JS中,setTimeout任务被放进事件队列中,只有主线程执行完才会去检查事件队列中的任务是否需要执行,因此setTimeout的实际执行时间可能会比其设定的时间晚一些。

  • 刷新频率受屏幕分辨率和屏幕尺寸的影响,因此不同设备的刷新频率可能会不同,而setTimeout只能设置一个固定时间间隔,这个时间不一定和屏幕的刷新时间相同。

以上两种情况都会导致setTimeout的执行步调和屏幕的刷新步调不一致。

在setTimeout中对dom进行操作,必须要等到屏幕下次绘制时才能更新到屏幕上,如果两者步调不一致,就可能导致中间某一帧的操作被跨越过去,而直接更新下一帧的元素,从而导致丢帧现象。

使用 requestAnimationFrame

与setTimeout相比,requestAnimationFrame最大的优势是由系统来决定回调函数的执行时机。

如果屏幕刷新率是60Hz,那么回调函数就每16.7ms被执行一次,如果刷新率是75Hz,那么这个时间间隔就变成了1000/75=13.3ms,换句话说就是,requestAnimationFrame的步伐跟着系统的刷新步伐走。它能保证回调函数在屏幕每一次的刷新间隔中只被执行一次,这样就不会引起丢帧现象。

我们使用requestAnimationFrame来进行分批渲染:

<ul id="container"></ul>
复制代码
//需要插入的容器
let ul = document.getElementById('container');
// 插入十万条数据
let total = 100000;
// 一次插入 20 条
let once = 20;
//总页数
let page = total/once
//每条记录的索引
let index = 0;
//循环加载数据
function loop(curTotal,curIndex){
 if(curTotal <= 0){
 return false;
 }
 //每页多少条
 let pageCount = Math.min(curTotal , once);
 window.requestAnimationFrame(function(){
 for(let i = 0; i < pageCount; i++){
 let li = document.createElement('li');
 li.innerText = curIndex + i + ' : ' + ~~(Math.random() * total)
 ul.appendChild(li)
 }
 loop(curTotal - pageCount,curIndex + pageCount)
 })
}
loop(total,index);
复制代码

看下效果

"Senior front-end" high-performance rendering one hundred thousand data (time slicing)


我们可以看到,页面加载的速度很快,并且滚动的时候,也很流畅没有出现闪烁丢帧的现象。

这就结束了么,还可以再优化么?

当然~~

使用 DocumentFragment

先解释一下什么是 DocumentFragment ,文献引用自MDN

DocumentFragment,文档片段接口,表示一个没有父级文件的最小文档对象。它被作为一个轻量版的Document使用,用于存储已排好版的或尚未打理好格式的XML片段。最大的区别是因为DocumentFragment不是真实DOM树的一部分,它的变化不会触发DOM树的(重新渲染) ,且不会导致性能等问题。

可以使用document.createDocumentFragment方法或者构造函数来创建一个空的DocumentFragment

从MDN的说明中,我们得知DocumentFragments是DOM节点,但并不是DOM树的一部分,可以认为是存在内存中的,所以将子元素插入到文档片段时不会引起页面回流。

最后修改代码如下:

<ul id="container"></ul>
复制代码
//需要插入的容器
let ul = document.getElementById('container');
// 插入十万条数据
let total = 100000;
// 一次插入 20 条
let once = 20;
//总页数
let page = total/once
//每条记录的索引
let index = 0;
//循环加载数据
function loop(curTotal,curIndex){
 if(curTotal <= 0){
 return false;
 }
 //每页多少条
 let pageCount = Math.min(curTotal , once);
 window.requestAnimationFrame(function(){
 let fragment = document.createDocumentFragment();
 for(let i = 0; i < pageCount; i++){
 let li = document.createElement('li');
 li.innerText = curIndex + i + ' : ' + ~~(Math.random() * total)
 fragment.appendChild(li)
 }
 ul.appendChild(fragment)
 loop(curTotal - pageCount,curIndex + pageCount)
 })
}
loop(total,index);
复制代码

At last

More herein it is to provide an idea of ​​time slicing mode by simultaneously loading a number of simple DOM. In the case of complex DOM, generally it will use virtual list of ways to achieve, on this issue, will continue to organize, so stay tuned.

"Senior front-end" high-performance rendering one hundred thousand data (time slicing)


Guess you like

Origin blog.51cto.com/14516511/2437486