`
deng131
  • 浏览: 673872 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

如何减少浏览器的repaint和reflow?

阅读更多
转自:http://varnow.org/?p=232
参考:http://www.stubbornella.org/content/2009/03/27/reflows-repaints-css-performance-making-your-javascript-slow/

文本内容主要包括以下几点:

   1. 什么是repaint/reflow?
   2. 什么情况下会触发浏览器的repaint/reflow?
   3. 浏览器自身所作的优化
   4. 如何优化你的脚本来减少repaint/reflow?

一、什么是repaint/reflow?

页面在加载的过程中,需要对文档结构进行解析,同时需要结合各种各样的样式来计算这个页面长什么样子,最后再经过浏览器的渲染页面就出现了。这整个过程细说起来还是比较复杂,其中充满了repaint和reflow。对于DOM结构中的各个元素都有自己的盒子(模型),这些都需要浏览器根据各种样式(浏览器的、开发人员定义的等)来计算并根据计算结果将元素放到它该出现的位置,这个过程称之为reflow;当各种盒子的位置、大小以及其他属性,例如颜色、字体大小等都确定下来后,浏览器于是便把这些元素都按照各自的特性绘制了一遍,于是页面的内容出现了,这个过程称之为repaint。

以上提到的只是在页面加载时必然会出现的repaint和reflow,除此之外,在页面加载完成后,用户的一些操作、脚本的一些操作都会导致浏览器发生这种行为,具体在后文阐述。

另外,关于浏览器渲染的更为详细的资料可以参考以下,涵盖了IE以及Firefox:

Understanding Internet Explorer Rendering Behaviour

Notes on HTML Reflow
二、什么情况下会触发浏览器的repaint/reflow?

除了页面在首次加载时必然要经历该过程之外,还有以下行为会触发这个行为:

    * DOM元素的添加、修改(内容)、删除( Reflow + Repaint)
    * 仅修改DOM元素的字体颜色(只有Repaint,因为不需要调整布局)
    * 应用新的样式或者修改任何影响元素外观的属性
    * Resize浏览器窗口、滚动页面
    * 读取元素的某些属性(offsetLeft、offsetTop、offsetHeight、offsetWidth、 scrollTop/Left/Width/Height、clientTop/Left/Width/Height、 getComputedStyle()、currentStyle(in IE))

在继续下面的文章之前,先介绍一款强大的性能分析工具-dynaTrace,借助该功能能够清晰的得到页面中的资源消耗情况,从而对症下药。另外,更细节的方面是它可以跟踪每个函数调用所造成的CPU消耗、Repaint/Reflow。接下来就借助该工具来测试一下以上描述的几点情况。
DOM元素的增删改

先看代码
view plaincopy to clipboardprint?

   1. <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3c.org/TR/html4/strict.dtd"> 
   2. <html> 
   3.     <body> 
   4.         <div id="test1" onclick="addNode()">这里是第1个节点</div> 
   5.         <div id="test2" onclick="modNode()">这里是第2个节点</div> 
   6.         <div id="test3" onclick="delNode()">这里是第3个节点</div> 
   7.     </body> 
   8.     <script type="text/javascript"> 
   9.         function $(id){ 
  10.             return document.getElementById(id); 
  11.         } 
  12.         function addNode(){ 
  13.             var n = document.createElement('div'); 
  14.             n.innerHTML = 'New Node'; 
  15.             $('test1').appendChild(n); 
  16.         } 
  17.         function modNode(){ 
  18.             $('test2').innerHTML = 'hello'; 
  19.         } 
  20.         function delNode(){ 
  21.             $('test3').parentNode.removeChild($('test3')); 
  22.         } 
  23.     </script> 
  24. </html> 

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3c.org/TR/html4/strict.dtd">
<html>
    <body>
        <div id="test1" onclick="addNode()">这里是第1个节点</div>
        <div id="test2" onclick="modNode()">这里是第2个节点</div>
        <div id="test3" onclick="delNode()">这里是第3个节点</div>
    </body>
    <script type="text/javascript">
        function $(id){
            return document.getElementById(id);
        }
        function addNode(){
            var n = document.createElement('div');
            n.innerHTML = 'New Node';
            $('test1').appendChild(n);
        }
        function modNode(){
            $('test2').innerHTML = 'hello';
        }
        function delNode(){
            $('test3').parentNode.removeChild($('test3'));
        }
    </script>
</html>

在依次点击完每一个按钮后,我们来看看dynaTrace的情况,首先是一目了然的点击事件分布

image

放大之后来看一下每个事件的repaint/reflow情况:

增加节点:

image

修改节点:

image

删除节点:

image

图中的绿色部分表示的是reflow和repaint过程,其中比较短的绿条标示的reflow过程,后面长条部分表示的是repaint过程。从图中可以看出,对DOM节点的增删改都会造成reflow和repaint,由于改动小所以reflow消耗的时间很短,但是由于repaint是全局的,因此消耗的时间都比较长。
修改DOM元素前景色
view plaincopy to clipboardprint?

   1. var n = $('colorNode'); 
   2. n.style.color = 'red'; 

var n = $('colorNode');
n.style.color = 'red';

image

从上图中可以看到修改字体颜色后,浏览器只有repaint而没有reflow。接下来试试修改背景色:
view plaincopy to clipboardprint?

   1. var n = $('colorNode'); 
   2. n.style.backgroundColor = 'red'; 

var n = $('colorNode');
n.style.backgroundColor = 'red';

image

由图中可以看出,修改背景色也会造成reflow和repaint。另外,经过测试发现,只要是修改元素的cssText属性,不论它的值是什么,都会导致浏览器reflow和repaint,因此在某些时候选择特定的样式属性赋值会有更好的效果。
Resize浏览器窗口以及拖动滚动条

image

测试中的操作如下:缩小浏览器窗口->放大浏览器窗口->拖动页面滚动条至页面底部。从图中可以看到Resize浏览器窗口以及拖动滚动条都会造成浏览器的repaint,而且CPU的消耗也比较大,尤其是拖动滚动条的时候。
读取Layout属性

根据各种参考资料中的描述,在用Javascript读取DOM节点的Layout属性(offsetLeft、offsetTop、 offsetHeight、offsetWidth、scrollTop/Left/Width/Height、clientTop/Left /Width/Height、getComputedStyle()、currentStyle(in IE)) 的时候也会触发repaint,不过在以下的测试例子中并没有发现这一点。
view plaincopy to clipboardprint?

   1. var n = $('colorNode'); 
   2. var temp = document.documentElement.currentStyle; 
   3. temp = n.offsetTop; 
   4. temp = n.offsetLeft; 
   5. temp = n.offsetWidth; 
   6. temp = n.offsetHeight; 
   7. temp = n.scrollTop; 
   8. temp = n.scrollHeight; 
   9. alert(temp); 

var n = $('colorNode');
var temp = document.documentElement.currentStyle;
temp = n.offsetTop;
temp = n.offsetLeft;
temp = n.offsetWidth;
temp = n.offsetHeight;
temp = n.scrollTop;
temp = n.scrollHeight;
alert(temp);

image

三、浏览器优化

浏览器对于每一个渲染动作并不是立即执行,而是维护了一个渲染任务队列,浏览器会根据具体的需要分批集中执行其中的任务。除了浏览器自身维护的定期调度之外,脚本中的某些操作会导致浏览器立即执行渲染任务,例如读取元素的Layout属性。
view plaincopy to clipboardprint?

   1. var bodystyle = document.body.style; 
   2. var computed; 
   3. if (document.body.currentStyle) { 
   4.   computed = document.body.currentStyle; 
   5. } else { 
   6.   computed = document.defaultView.getComputedStyle(document.body, ''); 
   7. } 
   8.  
   9. //每次都读取 
  10.  
  11. bodystyle.color = 'red'; 
  12. bodystyle.padding = '1px'; 
  13. tmp = computed.backgroundColor; 
  14. bodystyle.color = 'white'; 
  15. bodystyle.padding = '2px'; 
  16. tmp = computed.backgroundImage; 
  17. bodystyle.color = 'green'; 
  18. bodystyle.padding = '3px'; 
  19. tmp = computed.backgroundAttachment; 
  20.  
  21. //最后再读取 
  22.  
  23. bodystyle.color = 'yellow'; 
  24. bodystyle.padding = '4px'; 
  25. bodystyle.color = 'pink'; 
  26. bodystyle.padding = '5px'; 
  27. bodystyle.color = 'blue'; 
  28. bodystyle.padding = '6px'; 
  29. tmp = computed.backgroundColor; 
  30. tmp = computed.backgroundImage; 
  31. tmp = computed.backgroundAttachment; 

var bodystyle = document.body.style;
var computed;
if (document.body.currentStyle) {
  computed = document.body.currentStyle;
} else {
  computed = document.defaultView.getComputedStyle(document.body, '');
}

//每次都读取

bodystyle.color = 'red';
bodystyle.padding = '1px';
tmp = computed.backgroundColor;
bodystyle.color = 'white';
bodystyle.padding = '2px';
tmp = computed.backgroundImage;
bodystyle.color = 'green';
bodystyle.padding = '3px';
tmp = computed.backgroundAttachment;

//最后再读取

bodystyle.color = 'yellow';
bodystyle.padding = '4px';
bodystyle.color = 'pink';
bodystyle.padding = '5px';
bodystyle.color = 'blue';
bodystyle.padding = '6px';
tmp = computed.backgroundColor;
tmp = computed.backgroundImage;
tmp = computed.backgroundAttachment;

每次读取的渲染图:

image

最后读取的渲染图:

image
四、如何优化你的脚本来减少reflow/repaint?

1. 避免在document上直接进行频繁的DOM操作,如果确实需要可以采用off-document的方式进行,具体的方法包括但不完全包括以下几种:

    (1). 先将元素从document中删除,完成修改后再把元素放回原来的位置

    (2). 将元素的display设置为”none”,完成修改后再把display修改为原来的值

    (3). 如果需要创建多个DOM节点,可以使用DocumentFragment创建完后一次性的加入document

view plaincopy to clipboardprint?

   1. function appendEveryTime(){ 
   2.     for( var i = 5000; i--; ){ 
   3.         var n = document.createElement('div'); 
   4.         n.innerHTML = 'node ' + i; 
   5.         document.body.appendChild(n);/*每次创建的新节点都append到文档*/ 
   6.     } 
   7. } 
   8.  
   9. function appendLast(){ 
  10.      var frag = document.createDocumentFragment(); 
  11.      for( var i = 5000; i--; ){ 
  12.          var n = document.createElement('div'); 
  13.          n.innerHTML = 'node ' + i; 
  14.          frag.appendChild(n);/*每次创建的节点先放入DocumentFragment中*/ 
  15.      } 
  16.      document.body.appendChild(frag); 
  17. } 

function appendEveryTime(){
    for( var i = 5000; i--; ){
        var n = document.createElement('div');
        n.innerHTML = 'node ' + i;
        document.body.appendChild(n);/*每次创建的新节点都append到文档*/
    }
}

function appendLast(){
     var frag = document.createDocumentFragment();
     for( var i = 5000; i--; ){
         var n = document.createElement('div');
         n.innerHTML = 'node ' + i;
         frag.appendChild(n);/*每次创建的节点先放入DocumentFragment中*/
     }
     document.body.appendChild(frag);
}

用dynaTrace观察的结果如下,appendLast的性能无论是在Javascript的执行时间以及浏览器渲染时间方面都优于 appendEveryTime。

appendEveryTime:

image

appendLast:

image

2. 集中修改样式

    (1). 尽可能少的修改元素style上的属性

    (2). 尽量通过修改className来修改样式

    (3). 通过cssText属性来设置样式值

如下的代码中,每一次赋值都会造成浏览器重新渲染,可以采用cssText或者className的方式
view plaincopy to clipboardprint?

   1. el.style.color = 'red;
   2. el.style.height = '100px'; 
   3. el.style.fontSize = '12px';
   4. el.style.backgroundColor = 'white'; 

el.style.color = 'red;
el.style.height = '100px';
el.style.fontSize = '12px';
el.style.backgroundColor = 'white';

3. 缓存Layout属性值

对于Layout属性中非引用类型的值(数字型),如果需要多次访问则可以在一次访问时先存储到局部变量中,之后都使用局部变量,这样可以避免每次读取属性时造成浏览器的渲染。
view plaincopy to clipboardprint?

   1. var width = el.offsetWidth; 
   2. var scrollLeft = el.scrollLeft; 

var width = el.offsetWidth;
var scrollLeft = el.scrollLeft;

4. 设置元素的position为absolute或fixed

在元素的position为static和relative时,元素处于DOM树结构当中,当对元素的某个操作需要重新渲染时,浏览器会渲染整个页面。将元素的position设置为absolute和fixed可以使元素从DOM树结构中脱离出来独立的存在,而浏览器在需要渲染时只需要渲染该元素以及位于该元素下方的元素,从而在某种程度上缩短浏览器渲染时间,这在当今越来越多的Javascript动画方面尤其值得考虑。
view plaincopy to clipboardprint?

   1. <body style="position:relative"> 
   2.     <div id="test" style="background-color:red;width:100px;position:relative;">Animation Here</div> 
   3. </body> 
   4. <script type="text/javascript"> 
   5.     function $(id){ 
   6.         return document.getElementById(id); 
   7.     } 
   8.     window.onload = function(){ 
   9.         var t = $('test'); 
  10.  
  11.         ~function(){ 
  12.             tt.style.left = t.offsetLeft + 5 + 'px'; 
  13.             tt.style.height = t.offsetHeight + 5 + 'px'; 
  14.             setTimeout(arguments.callee,500); 
  15.         }(); 
  16.     } 
  17. </script> 

<body style="position:relative">
<div id="test" style="background-color:red;width:100px;position:relative;">Animation Here</div>
</body>
<script type="text/javascript">
function $(id){
return document.getElementById(id);
}
window.onload = function(){
var t = $('test');

~function(){
t.style.left = t.offsetLeft + 5 + 'px';
t.style.height = t.offsetHeight + 5 + 'px';
setTimeout(arguments.callee,500);
}();
}
</script>

通过修改#test元素的postion为relative和postion分别得到如下两个测试结果

position: relative

image

position: absolute

image

在postion:relative的测试当中,浏览器在重新渲染时做的工作比position:absolute多了不少。

参考资料

Understanding Internet Explorer Rendering Behaviour

Notes on HTML Reflow

EFFICIENT JAVASCRIPT
WEB优化reflow, repaint, 性能优化

You can follow any responses to this entry through the RSS 2.0 feed. You can leave a response, or trackback from your own site.
分享到:
评论

相关推荐

    如何减少浏览器的reflow和repaint

    总的来说,理解`reflow`和`repaint`的原理,并通过优化DOM操作、样式修改、缓存属性和利用定位属性等方式,可以有效地减少浏览器的渲染负担,提高网页性能。在实际开发中,开发者应当养成良好的编程习惯,时刻关注...

    JavaScript中的Repaint和Reflow用法详解

    你是不是经常听师兄或一些前端前辈说不能用CSS通配符 *...1.什么是Repaint/Reflow? 好,先上一张图:浏览器解析大概的工作流程 这张图应该可以很好理解,归纳为四个步骤: 1、解析HTML以构建DOM树:渲染引擎开始解析H

    有关javascript的性能优化 (repaint和reflow)

    **如何避免或减少Repaint和Reflow:** 1. **离线操作**:先移除元素,完成修改后再放回,减少对页面的影响。 2. **隐藏元素**:通过设置`display: none`,修改后再恢复,避免实时计算布局。 3. **使用...

    sisterAn#blog#浏览器的重绘与回流(Repaint、Reflow)1

    1. 浏览器渲染机制 2. 重绘 3. 回流 4. 浏览器优化 5. 减少重绘与回流

    浏览器渲染过程及优化策略

    浏览器渲染优化策略包括减少 repaint 和 reflow、合理使用缓存、使用 CSS 动画、避免过多的 DOM 操作、使用 lazy loading 等。这些策略可以减少浏览器的渲染时间,提高浏览器的性能和用户体验。 在浏览器渲染过程中...

    渲染篇 5:最后一击——回流(Reflow)与重绘(Repaint)(1).md

    在前端性能优化的领域中,回流(Reflow)与重绘(Repaint)是非常关键的概念。它们是浏览器渲染过程中最常见、也最影响性能的两个步骤。为了深入理解这两个概念,我们首先需要了解浏览器的渲染流程。 在用户通过...

    HTML解析原理.pdf

    reflow和repaint是浏览器渲染过程中的重要环节,频繁的reflow会显著降低页面性能。因此,优化页面渲染的关键在于减少不必要的reflow。例如,为图片预先设定宽度和高度可以避免图片加载后引起的布局变化。此外,尽量...

    WEB前端工程师Web前端性能优化经验分享

    2. 正确理解Repaint和Reflow Repaint(重绘)是指元素外观变化但不影响布局时发生的情况,例如改变颜色或边框。Reflow(重排)则是布局或几何属性发生变化时,浏览器需要重新计算元素及关联元素的布局。频繁的Reflow...

    浏览器渲染过程与性能优化1

    这些过程通常被称为重排(Reflow)和重绘(Repaint),过多的重排和重绘会导致性能下降,因此应尽量避免不必要的操作。 性能优化可以从多个角度进行。例如,利用CSS的`display: none`或`visibility: hidden`来隐藏...

    HTML解析原理.docx

    浏览器的渲染过程涉及多个步骤,包括解析HTML、CSS、JavaScript以及处理图片等资源,其中关键环节是页面的渲染、重绘(repaint)和...在实际开发中,应尽可能减少reflow和repaint的次数,以优化页面加载和交互速度。

    新版前端高频面试题笔记+课件+源码(HTML+CSS+JavaScript)

    本视频主打内容最全最新,包括大前端基础与高频面试题,打造一站式知识长龙 服务,适合有前端基础的同学学习...2.如何最小化重绘(repaint)和回流(reflow)?3.Javascript作用域链? 4.数据请求 5.跨域和同源策略6.面向对象

    在浏览器输入一个网址发生了什么-阿沐1

    - 解析过程中,CSS和HTML可以并行加载,但JS执行会阻塞HTML解析,防止页面布局频繁变化(repaint和reflow)。 - 浏览器根据渲染树进行布局计算,确定每个节点的位置和大小,然后绘制到屏幕上,最终展示给用户。 ...

    前端浏览器的工作流程详解

    在布局计算完成后,浏览器会进行回流(reflow)和重绘(repaint)两个重要步骤。回流指的是浏览器为了确定元素的位置和尺寸而进行的计算过程,这通常是在初次加载页面或进行DOM操作时发生。重绘则是当元素样式改变但...

    渲染篇 2:知己知彼——解锁浏览器背后的运行机制(2).md

    - **减少重排(Reflow)和重绘(Repaint):** 重排和重绘是浏览器渲染过程中的两个关键性能消耗点。重排是当元素的布局改变时发生的,而重绘是当元素的样式改变(不涉及布局变化)时发生的。开发者应当尽量避免这两...

    处理浏览器内核信息的方法及设备.zip

    - **渲染优化**:减少重排(reflow)和重绘(repaint),避免不必要的DOM操作。 - **JavaScript优化**:合理使用异步加载,避免阻塞页面渲染,利用闭包和作用域控制变量。 - **资源加载**:利用CDN加速,合并和...

    「资深前端工程师总结」前端面试知识点大全 —CSS 篇

    Repaint 和 Reflow Repaint 是指浏览器重新绘制某个元素的过程,通常发生在元素的外观被改变时。Reflow 是指浏览器重新计算 Render Tree 的过程,通常发生在元素的布局被改变时。 display 属性的其他值 除了 none...

    用css3实现转换过渡和动画效果

    导致js效率低的两个因素都包括在内了在频繁的操作DOM和CSS时,浏览器会不停的执行重排和重绘,在PC版本的浏览器中,因为浏览器可用的内存比较大,用户肉眼几乎看不见页面动画产生的repaint和reflow,所以工程师几乎...

    repaint

    这个过程就包括了`repaint`和相关的概念,如`reflow`。 **重涂(Repaint)**: 重涂是指元素的视觉属性发生变化但其几何尺寸(宽度、高度)保持不变时,浏览器只需重新绘制该元素的新外观,而无需调整周围元素的...

Global site tag (gtag.js) - Google Analytics