Home > Article > Web Front-end > Optimization methods for page redrawing and reflow
Before the discussion page is redrawn and reflowed. You need to have some understanding of the page rendering process, how the page displays html combined with css, etc. to the browser. The following flow chart shows the browser's processing flow for page rendering. Different browsers may be slightly different. But basically they are similar.
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 commonly used document object. The DOM tree contains all HTML tags, including display:none hidden, elements dynamically added using JS, etc.
The browser parses all styles (user-defined CSS and user agents) into style structures. During the parsing process, styles that the browser cannot recognize will be removed. For example, IE will remove them. Styles starting with -moz, while FF will remove styles starting with _.
3. The DOM Tree and the style structure are combined to build a render tree. The render tree is similar to the DOM tree, but the difference is very big. The render tree can recognize the style. Each NODE in the render tree Each has its own style, and the 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 will not affect the rendering, they will not be included. in render tree. Note that elements hidden by 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 the render tree is called Box (Box dimensions), and the page element is understood to be a box with padding, margins, borders and position.
Once the render tree is constructed, the browser can draw the page based on the render tree.
Reflow and redraw
When part (or all) of the render tree is due to the size of the element, Layout, hiding, etc. changes and need to be rebuilt. This is called reflow. Every page needs to be reflowed at least once, which is when the page loads for the first time. During reflow, the browser will invalidate the affected part of the rendering tree and reconstruct this part of the rendering tree. After completing the reflow, the browser will redraw the affected part to the screen. This process is called redrawing.
When some elements in the render tree need to update attributes, these attributes only affect the appearance and style of the elements, but will not affect the layout, such as background-color. It is called redrawing.
Note: Reflow will definitely cause redraw, but redraw will not necessarily cause reflow.
When does reflow occur:
Reflow is required when the page layout and geometric properties change. Browser reflow will occur in the following situations:
1. Adding or deleting visible DOM elements;
2. The position of the element changes;
3. The size of the element changes—— Margins, padding, borders, width and height
4. Content changes - such as changes in calculated value width and height caused by text changes or image size changes;
5. Page rendering initialization ;
6. The browser window size changes - when the resize event occurs;
Let us see how the following code affects reflow and redrawing:
var s = document.body.style; s.padding = "2px"; // 回流+重绘 s.border = "1px solid red"; // 再一次 回流+重绘 s.color = "blue"; // 再一次重绘 s.backgroundColor = "#ccc"; // 再一次 重绘 s.fontSize = "14px"; // 再一次 回流+重绘// 添加node,再一次 回流+重绘 document.body.appendChild(document.createTextNode('abc!'));
Having said this, everyone knows that reflow is more expensive than redrawing. The cost of reflow is related to how many nodes in the render tree need to be rebuilt. Suppose you directly operate the body, such as inserting an element at the front of the body, which will cause the entire Render tree reflow, of course the cost will be relatively high, but if you insert an element after the body, it will not affect the reflow of the previous element
Smart browser
From the previous example code, you can see that a few lines of simple JS code caused about 6 reflows and redraws. And we also know that the cost of reflow is not small. If every JS operation has to be reflowed and redrawn, the browser may not be able to bear it. Therefore, many browsers will optimize these operations. The browser will maintain a queue and put all operations that will cause reflow and redrawing into this queue. When the operations in the queue reach a certain number or a certain time interval, the browser The queue will be flushed and a batch will be processed. This will turn multiple reflows and redraws into one reflow and redraw.
Although there are browser optimizations, sometimes some of the code we write may force the browser to flush the queue in advance, so the browser optimization may not be effective. When you request some style information from the browser, the browser will flush the queue, such as:
offsetTop, offsetLeft, offsetWidth, offsetHeight scrollTop/Left/Width/Height clientTop/Left/Width/Height width,height 请求了getComputedStyle(), 或者 IE的 currentStyle
When you request some of the above attributes Sometimes, in order to give you the most accurate values, the browser needs to flush the queue, because there may be operations in the queue that affect these values. Even if the layout and style information you obtain for an element has nothing to do with the layout information that recently occurred or changed, the browser will forcefully refresh the rendering queue.
How to reduce reflow and redraw
减少回流、重绘 其实就是需要减少对 render tree 的操作(合并多次多DOM和样式的修改),并减少对一些style信息的请求,尽量利用好浏览器的优化策略。具体方法有:
直接改变className,如果动态改变样式,则使用cssText(考虑没有优化的浏览器)
// 不好的写法var left = 1;var top = 1; el.style.left = left + "px"; el.style.top = top + "px";// 比较好的写法el.className += " className1";// 比较好的写法el.style.cssText += "; left: " + left + "px; top: " + top + "px;";
让要操作的元素进行”离线处理”,处理完后一起更新
a) 使用 DocumentFragment 进行缓存操作,引发一次回流和重绘;
b) 使用 display:none 技术,只引发两次回流和重绘;
c) 使用 cloneNode(true or false) 和 replaceChild 技术,引发一次回流和重绘;
3.不要经常访问会引起浏览器 flush 队列的属性,如果你确实要访问,利用缓存
// 别这样写,大哥 for(循环) { el.style.left = el.offsetLeft + 5 + "px";el.style.top = el.offsetTop + 5 + "px";} // 这样写好点 var left = el.offsetLeft, top = el.offsetTop, s = el.style; for (循环) { left += 10; top += 10; s.left = left + "px"; s.top = top + "px"; }
让元素脱离动画流,减少回流的Render Tree的规模
$("#block1").animate({left:50}); $("#block2").animate({marginLeft:50});
实例测试
最后用2个工具对上面的理论进行一些测试,分别是:dynaTrace(测试ie),Speed Tracer(测试Chrome)。
第一个测试代码不改变元素的规则,大小,位置。只改变颜色,所以不存在回流,仅测试重绘,代码如下:
<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>
testOneByOne 函数改变3次color,其中每次改变后调用getComputedStyle,读取属性值(按我们上面的讨论,这里会引起队列的 flush),testAll 同样是改变3次color,但是每次改变后并不马上调用getComputedStyle。
我们先点击Test One by One按钮,然后点击 Test All,用dynaTrace监控如下:
上图可以看到我们执行了2次button的click事件,每次click后都跟一次rendering,2次click函数执行的时间都差不多, 0.25ms,0.26ms,但其后的rendering时间就相差一倍多。(其实很多时候,前端的性能瓶颈并不在于JS的执行,而是在于页面的呈现,这种情况在富客户端中更为突出)。我们再看图的下面部分,这是第一次rendering的详细信息,可以看到里面有2行是 Scheduleing layout task,这个就是我们前面讨论过的浏览器优化过的队列,可以看出我们引发2次的flush。
再看第二次rendering的详细信息,可以看出并没有Scheduleing layout task,所以这次rendering的时间也比较短。
测试代码2:这个测试跟第一次测试的代码很类似,但加上了对layout的改变,为的是测试回流。
<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>
这图可以看出,有了回流后,rendering的时间相比之前的只重绘,时间翻了3倍了,可见回流的高成本性啊。
大家看到时候注意明细处相比之前的多了个 Calcalating flow layout。
最后再使用Speed Tracer测试一下,其实结果是一样的,只是让大家了解下2个测试工具:
测试1:
图上第一次点击执行2ms (其中有50% 用于style Recalculation), 第二次1ms,而且第一次click后面也跟了2次style Recalculation,而第二次点击却没有style Recalculation。
但是这次测试发现paint重绘的时间竟然是一样的,都是3ms,这可能就是chrome比IE强的地方吧。
测试2:
从图中竟然发现第二次的测试结果在时间上跟第一次的完全一样,这可能是因为操作太少,而chrome又比较强大,所以没能测试明显结果出来。
但注意图中多了1个紫色部分,就是layout的部分。也就是我们说的回流。
The above is the detailed content of Optimization methods for page redrawing and reflow. For more information, please follow other related articles on the PHP Chinese website!