High performance web development page presentation, redrawing, reflow.

Time:2019-11-20

Page rendering process

Before the discussion page redraw and reflow. We need to understand the rendering process of the page. How the page displays HTML combined with CSS to the browser? The following flowchart shows the processing flow of the browser’s rendering of the page. Maybe different browsers will be slightly different. But it’s basically the same.

1. The browser parses the obtained HTML code into a DOM tree. Each tag in html is a node in the DOM tree. The root node is our common document object (< HTML > tag). DOM tree is the HTML structure we see with firebug or IE developer toolbar and other tools. It contains all HTML tags, including display: none hiding and dynamic elements added with JS.

2. The browser parses all styles (mainly including CSS and browser’s style settings) into style structures. In the process of parsing, the styles that the browser cannot recognize will be removed. For example, ie will remove the style beginning with – Moz, while Firefox will remove the style beginning with “Ou”.

3. When DOM tree and style structure are combined to build render tree, render tree is similar to DOM tree, but there are great differences. Render tree can recognize styles. Each node in render tree has its own style, and render tree does not contain hidden nodes (such as display: none nodes and head nodes), because these nodes will not be used for rendering, and they will not It will affect the rendering, so it will not be included in the render tree. Note that the hidden elements of visibility: hidden will still be included in the render tree, because visibility: hidden will affect the layout and occupy space. According to the CSS2 standard, each node in render tree is called box dimensions. All attributes of box are: width, height, margin, padding, left, top, border, etc.

4. Once the render tree is built, the browser can draw the page according to the render tree.

Reflow and redraw

1. When a part (or all) of render tree needs to be rebuilt due to the size, layout, hiding and other changes of elements. This is called reflow (in fact, I think it’s easier to call rearrangement). Each page needs at least one reflow, the first time the page is loaded.

2. When some elements in render tree need to update attributes, and these attributes only affect the appearance and style of elements, but not the layout, such as background color. It is called redraw.
Note: it can be seen from the above that reflow will certainly cause redrawing, while redrawing will not necessarily cause reflow.

What operation will cause redrawing and reflow
In fact, any operation on elements in render tree will cause reflow or redraw, such as:

1. Add and delete elements (reflow + redraw)

2. Hidden element, display: none (reflow + redraw), visibility: hidden (redraw only, no reflow)

3. Move elements, such as changing top and left (jQuery’s animate method is that changing top and left does not necessarily affect the reflow), or move elements to another parent element. (redraw + reflow)

4. Operation on style (different influence on different attribute operations)

5. There is also a user’s operation, such as changing the size of the browser, changing the font size of the browser, etc. (reflow + redraw)

Let’s see how the following code affects reflow and redraw:

Copy codeThe code is as follows:
var s = document.body.style;
S.padding = “2px”; / / reflow + redraw
S.border = “1px solid red”; / / reflow + redraw again
S.color = “blue”; / / redraw again
S.backgroundcolor = “ා CCC”; / / redraw again
S.fontsize = “14px”; / / reflow + redraw again
//Add node, reflow again + redraw
document.body.appendChild(document.createTextNode(‘abc!’));

Please note how many I used above again.

As we all know, the cost of reflow mapping is higher. The cost of reflow has something to do with how many nodes of render tree need to be rebuilt. If you operate the body directly, for example, inserting one element at the front of the body will cause the whole render tree to reflow. Of course, the cost will be higher, but if you insert one element after the body, it will not affect the previous elements Backflow.

Smart browser

From the last instance code, you can see that a few lines of simple JS code cause about six reflows and redraws. And we also know that the cost of reflow is not small. If you reflow and redraw every JS operation, the browser may not be able to bear it. So many browsers will optimize these operations. The browser will maintain a queue and put all operations that will cause backflow and redraw into the queue. When the number of operations in the queue reaches a certain amount or a certain time interval, the browser will put the flush queue into a batch processing. This will make multiple reflow, redraw into a reflow redraw.

Although there is browser optimization, sometimes some code we write may force the browser to flush the queue in advance, so the optimization of the browser may not work. When you request some style information from the browser, you will make the browser flush queue, such as:
    1. offsetTop, offsetLeft, offsetWidth, offsetHeight
    2. scrollTop/Left/Width/Height
    3. clientTop/Left/Width/Height
    4. width,height
5. Getcomputedstyle() or IE’s currentstyle is requested

When you request some of the above properties, the browser needs to flush the queue in order to give you the most accurate values, because there may be operations affecting these values in the queue.

How to reduce reflow and redraw

To reduce backflow and redraw, you need to reduce the operation of render tree, reduce the request for some style information, and make best use of the optimization strategy of browser. The specific methods are as follows:

1. Do not change the style attribute of an element. It is better to change the classname directly, but the classname is a predefined style, not dynamic. If you want to change some styles dynamically, use csstext to change them, as shown in the following code:

//Bad writing
var left = 1;
var top = 1;
el.style.left 
= left + px;
el.style.top  
= top  + px;

//A better way to write
el.className +=  className1;

//A better way to write
el.style.cssText += ; left:  + left + px; top:  + top + px;;

2. Make the elements to be operated “offline processing” and update them together after processing. The so-called “offline processing” here means that the elements do not exist in the render tree, for example:
UsedocumentFragmentOr div and other elements for cache operation. This is mainly used to add elements. You should have used it. First, add all the elements you want to add to a div (this div is also new),
Finally, the div is attached to the body.
B) first display: none hides the element, then perform all operations on the element, and finally display the element. Because the operation on the display: none element will not cause reflow and redraw. So as long as there are only two reflow operations.

3. Do not frequently access the properties that will cause the browser flush queue. If you really want to access them, read them into the variables for caching first, and then read the variables directly when you use them later. See the following code:

//Don’t write like that, brother
for(loop) {
    el.style.left 
= el.offsetLeft + 5 + px;
    el.style.top  
= el.offsetTop  + 5 + px;
}

//Write it better
var left = el.offsetLeft,top  = el.offsetTop,s = el.style;
for(loop) {
    left 
+= 10;
    top  
+= 10;
    s.left 
= left + px;
    s.top  
= top  + px;
}

4. Consider how many nodes in the render tree your operation will affect and how it will affect. The more influence, the more cost. For example, now many people use jQuery’s animate method to move elements to show some animation effects. Think of the following two methods of moving:

//Block1 is the element located by position: absolute, and its movement will affect all child elements under its parent element.
//Because in the process of moving, all sub elements need to judge whether the Z-index of block1 is on their own,
//If it’s on your own, you need to redraw it. There’s no reflow here
$(“#block1”).animate({left:50});
//Block2 is a relatively positioned element, which has the same influence as block1, but because block2 is not absolutely positioned
//What’s more, the marginleft attribute is changed, so each change here will not only affect the redraw,
//It also causes the reflow of the parent element and its lower elements
$(“#block2”).animate({marginLeft:50});

Case test

Finally, I use two tools to test the above theory. These two tools are recommended in my “web performance test tool recommendation” article, which are dynatrace (test IE) and speed tracker (test chrome).

The first test code does not change the rules, size, and location of elements. Only change the color, so there is no reflow, only test redraw, the code is as follows:

<body>
    <script type=”text/javascript”>
        var s = document.body.style;
        
var computed;
        
if (document.body.currentStyle) {
          computed 
= document.body.currentStyle;
        } 
else {
          computed 
= document.defaultView.getComputedStyle(document.body, ”);
        }
    
function testOneByOne(){
      s.color 
= ‘red’;;
      tmp 
= computed.backgroundColor;
      s.color 
= ‘white’;
      tmp 
= computed.backgroundImage;
      s.color 
= ‘green’;
      tmp 
= computed.backgroundAttachment;
    }

    function testAll() {
      s.color 
= ‘yellow’;
      s.color 
= ‘pink’;
      s.color 
= ‘blue’;

      tmp = computed.backgroundColor;
      tmp 
= computed.backgroundImage;
      tmp 
= computed.backgroundAttachment;
    }
    
</script>    
    color test 
<br />
    <button onclick=”testOneByOne()”>Test One by One</button>
    <button onclick=”testAll()”>Test All</button>
</body>        

    testOneByOneThe function changes 3 times color, which is called after each change.getComputedStyle, read the attribute value (as we discussed above, this will cause the flush of the queue),testAllAlso change color three times, but do not call immediately after each changegetComputedStyle
We first click the test one by one button, then click test all, and use dynatrace to monitor as follows:

   

As shown in the figure above, we have executed the button click event twice. Each click is followed by a rendering (page redraw). The execution time of the click function twice is almost 0.25ms, 0.26ms, but the rendering time after that is more than twice as long. (it can also be seen here that in fact, the performance bottleneck of the front-end is not the execution of JS, but the presentation of pages, which is more prominent in using JS to achieve rich clients). Let’s look at the following part of the figure again. This is the details of the first rendering. You can see that there are two lines of scheduling layout task. This is the queue optimized by the browser we discussed earlier. You can see that we have triggered two times of flush.
   

   

If you look at the details of the second rendering, you can see that there is no scheduling layout task, so the rendering time is relatively short.

Test code 2: this test is similar to the code of the first test, but the change of layout is added to test the reflow.

<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN” “http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”>
<html xmlns=”http://www.w3.org/1999/xhtml”>
<head>
</head>
<body>
    <script type=”text/javascript”>
        var s = document.body.style;
        
var computed;
        
if (document.body.currentStyle) {
          computed 
= document.body.currentStyle;
        } 
else {
          computed 
= document.defaultView.getComputedStyle(document.body, ”);
        }
    
function testOneByOne(){
      s.color 
= ‘red’;
      s.padding 
= ‘1px’;
      tmp 
= computed.backgroundColor;
      s.color 
= ‘white’;
      s.padding 
= ‘2px’;
      tmp 
= computed.backgroundImage;
      s.color 
= ‘green’;
      s.padding 
= ‘3px’;
      tmp 
= computed.backgroundAttachment;
    }

    function testAll() {
      s.color 
= ‘yellow’;
      s.padding 
= ‘4px’;
      s.color 
= ‘pink’;
      s.padding 
= ‘5px’;
      s.color 
= ‘blue’;
      s.padding 
= ‘6px’;

      tmp = computed.backgroundColor;
      tmp 
= computed.backgroundImage;
      tmp 
= computed.backgroundAttachment;
    }
    
</script>    
    color test 
<br />
    <button onclick=”testOneByOne()”>Test One by One</button>
    <button onclick=”testAll()”>Test All</button>
</body>        

Use dynatrace to monitor as follows:

  

I don’t need to say that everyone can understand this picture. It can be seen that the rendering time after reflow is three times longer than before, which shows the high cost of reflow.
Notice that there are more calculating flow layouts in details than before.

Finally, use speed tracker to test. The results are the same, just to let you know the following two test tools:

Test 1:

 

In the figure, the first click is used for 2ms (50% of which is used for style calculation), the second click is used for 1ms, and the first click is followed by style calculation twice, while the second click is not used for style calculation.
But this test found that the time of paint redrawing is the same, all of which are 3MS. This may be the place where Chrome is better than ie.

Test 2:

 

 
It is found that the second test result is exactly the same as the first one in terms of time. This may be because there are too few operations and chrome is relatively powerful, so the obvious results can not be tested,
But notice that there is an extra purple part in the figure, that is, the layout part. That’s what we call reflow.
[statement] for reprint, please indicate the source: http://www.blogjava.net/bearrui/ No commercial use!