一、什么是repaint/reflow?
页面在加载的过程中,需要对文档结构进行解析,同时需要结合各种各样的样式来计算这个页面长什么样子,最后再经过浏览器的渲染页面就出现了。这整个过程细说起来还是比较复杂,其中充满了repaint和reflow。对于DOM结构中的各个元素都有自己的盒子(模型),这些都需要浏览器根据各种样式(浏览器的、开发人员定义的等)来计算并根据计算结果将元素放到它该出现的位置,这个过程称之为reflow;当各种盒子的位置、大小以及其他属性,例如颜色、字体大小等都确定下来后,浏览器于是便把这些元素都按照各自的特性绘制了一遍,于是页面的内容出现了,这个过程称之为repaint。
以上提到的只是在页面加载时必然会出现的repaint和reflow,除此之外,在页面加载完成后,用户的一些操作、脚本的一些操作都会导致浏览器发生这种行为,具体在后文阐述。
另外,关于浏览器渲染的更为详细的资料可以参考以下,涵盖了IE以及Firefox:
Understanding Internet Explorer Rendering Behaviour
二、什么情况下会触发浏览器的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元素的增删改
先看代码
HTML:
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')); }
在依次点击完每一个按钮后,我们来看看dynaTrace的情况,首先是一目了然的点击事件分布
放大之后来看一下每个事件的repaint/reflow情况:
增加节点:
修改节点:
删除节点:
图中的绿色部分表示的是reflow和repaint过程,其中比较短的绿条标示的reflow过程,后面长条部分表示的是repaint过程。从图中可以看出,对DOM节点的增删改都会造成reflow和repaint,由于改动小所以reflow消耗的时间很短,但是由于repaint是全局的,因此消耗的时间都比较长。
修改DOM元素前景色
var n = $('colorNode'); n.style.color = 'red';
从上图中可以看到修改字体颜色后,浏览器只有repaint而没有reflow。接下来试试修改背景色:
var n = $('colorNode'); n.style.backgroundColor = 'red';
由图中可以看出,修改背景色也会造成reflow和repaint。另外,经过测试发现,只要是修改元素的cssText属性,不论它的值是什么,都会导致浏览器reflow和repaint,因此在某些时候选择特定的样式属性赋值会有更好的效果。
Resize浏览器窗口以及拖动滚动条
测试中的操作如下:缩小浏览器窗口->放大浏览器窗口->拖动页面滚动条至页面底部。从图中可以看到Resize浏览器窗口以及拖动滚动条都会造成浏览器的repaint,而且CPU的消耗也比较大,尤其是拖动滚动条的时候。
读取Layout属性
根据各种参考资料中的描述,在用Javascript读取DOM节点的Layout属性(offsetLeft、offsetTop、offsetHeight、offsetWidth、scrollTop/Left/Width/Height、clientTop/Left/Width/Height、getComputedStyle()、currentStyle(in IE)) 的时候也会触发repaint,不过在以下的测试例子中并没有发现这一点。
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);
三、浏览器优化
浏览器对于每一个渲染动作并不是立即执行,而是维护了一个渲染任务队列,浏览器会根据具体的需要分批集中执行其中的任务。除了浏览器自身维护的定期调度之外,脚本中的某些操作会导致浏览器立即执行渲染任务,例如读取元素的Layout属性。
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;
每次读取的渲染图:
最后读取的渲染图:
四、如何优化你的脚本来减少reflow/repaint?
1. 避免在document上直接进行频繁的DOM操作,如果确实需要可以采用off-document的方式进行,具体的方法包括但不完全包括以下几种:
(1). 先将元素从document中删除,完成修改后再把元素放回原来的位置
(2). 将元素的display设置为”none”,完成修改后再把display修改为原来的值
(3). 如果需要创建多个DOM节点,可以使用DocumentFragment创建完后一次性的加入document
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:
appendLast:
2. 集中修改样式
(1). 尽可能少的修改元素style上的属性
(2). 尽量通过修改className来修改样式
(3). 通过cssText属性来设置样式值
如下的代码中,每一次赋值都会造成浏览器重新渲染,可以采用cssText或者className的方式
el.style.color = 'red; el.style.height = '100px'; el.style.fontSize = '12px'; el.style.backgroundColor = 'white';
3. 缓存Layout属性值
对于Layout属性中非引用类型的值(数字型),如果需要多次访问则可以在一次访问时先存储到局部变量中,之后都使用局部变量,这样可以避免每次读取属性时造成浏览器的渲染。
var width = el.offsetWidth; var scrollLeft = el.scrollLeft;
4. 设置元素的position为absolute或fixed
在元素的position为static和relative时,元素处于DOM树结构当中,当对元素的某个操作需要重新渲染时,浏览器会渲染整个页面。将元素的position设置为absolute和fixed可以使元素从DOM树结构中脱离出来独立的存在,而浏览器在需要渲染时只需要渲染该元素以及位于该元素下方的元素,从而在某种程度上缩短浏览器渲染时间,这在当今越来越多的Javascript动画方面尤其值得考虑。
HTML代码:
Javascript代码:
var t = $('test'); ~function(){ t.style.left = t.offsetLeft + 5 + 'px'; t.style.height = t.offsetHeight + 5 + 'px'; setTimeout(arguments.callee,500); }();
通过修改#test元素的postion为relative和postion分别得到如下两个测试结果
position: relative
position: absolute
在postion:relative的测试当中,浏览器在重新渲染时做的工作比position:absolute多了不少。
相关推荐
总的来说,理解`reflow`和`repaint`的原理,并通过优化DOM操作、样式修改、缓存属性和利用定位属性等方式,可以有效地减少浏览器的渲染负担,提高网页性能。在实际开发中,开发者应当养成良好的编程习惯,时刻关注...
重绘通常对性能的影响相对较小,因为它只涉及到元素表面的更新。 **Reflow(渲染)**则更为复杂,它涉及到元素及其所有祖先元素的布局调整。当元素的尺寸、内边距、外边距、浮动状态等影响布局的属性改变时,浏览器...
此外,对DOM节点的属性值进行循环操作是一个性能低下的实践,因为每次读写操作都可能导致浏览器的重绘或重排。优先修改层级比较低的DOM节点也能够减少重排的范围,但有时候也会造成大面积的Reflow,因此需要具体情况...
这些过程通常被称为重排(Reflow)和重绘(Repaint),过多的重排和重绘会导致性能下降,因此应尽量避免不必要的操作。 性能优化可以从多个角度进行。例如,利用CSS的`display: none`或`visibility: hidden`来隐藏...
在前端性能优化的领域中,回流(Reflow)与重绘(Repaint)是非常关键的概念。它们是浏览器渲染过程中最常见、也最影响性能的两个步骤。为了深入理解这两个概念,我们首先需要了解浏览器的渲染流程。 在用户通过...
浏览器渲染优化策略包括减少 repaint 和 reflow、合理使用缓存、使用 CSS 动画、避免过多的 DOM 操作、使用 lazy loading 等。这些策略可以减少浏览器的渲染时间,提高浏览器的性能和用户体验。 在浏览器渲染过程中...
接下来,正确理解Repaint和Reflow对性能优化至关重要。Repaint指的是元素的视觉样式发生变化,如颜色、边框等,但不涉及布局;而Reflow则是元素布局发生变化,浏览器必须重新计算布局并更新渲染。Reflow通常是性能...
5. **回流(Reflow)与重绘(Repaint)**:"最后一击——回流与重绘"可能讨论了这两者对性能的影响,以及如何通过优化DOM操作来减少不必要的重排和重绘。 6. **事件的节流(throttle)与防抖(debounce)**:"事件...
reflow和repaint是浏览器渲染过程中的重要环节,频繁的reflow会显著降低页面性能。因此,优化页面渲染的关键在于减少不必要的reflow。例如,为图片预先设定宽度和高度可以避免图片加载后引起的布局变化。此外,尽量...
1. 理解性能优化的重要性:了解性能优化对用户体验和网站成功的直接影响。 2. 熟悉性能监控工具:如Google Lighthouse、PageSpeed Insights等,能够对网站进行性能评估和找出性能瓶颈。 3. 实践和积累经验:不断在...
在布局计算完成后,浏览器会进行回流(reflow)和重绘(repaint)两个重要步骤。回流指的是浏览器为了确定元素的位置和尺寸而进行的计算过程,这通常是在初次加载页面或进行DOM操作时发生。重绘则是当元素样式改变但...
4. **回流(reflow)与重绘(repaint)**:回流是指当页面元素的尺寸、位置发生改变时,浏览器需要重新计算元素及其子孙元素的几何属性和布局,这是一个昂贵的操作。重绘则是指元素外观的改变,如颜色、边框等,仅...
- **渲染优化**:减少重排(reflow)和重绘(repaint),避免不必要的DOM操作。 - **JavaScript优化**:合理使用异步加载,避免阻塞页面渲染,利用闭包和作用域控制变量。 - **资源加载**:利用CDN加速,合并和...
**回流**是指当元素的尺寸、位置或隐藏状态发生变化时,浏览器需要重新计算元素的位置和大小,以及它们对周围元素的影响,进而重新构建渲染树。这通常发生在页面加载时,以及添加或删除元素、更改元素尺寸、移动元素...
- **减少重排(Reflow)和重绘(Repaint):** 重排和重绘是浏览器渲染过程中的两个关键性能消耗点。重排是当元素的布局改变时发生的,而重绘是当元素的样式改变(不涉及布局变化)时发生的。开发者应当尽量避免这两...
例如,当一个按钮的背景色改变时,浏览器只需对按钮区域进行重涂,更新颜色显示。 **回流(Reflow)**: 与重涂不同,回流是元素的尺寸或布局发生变化时,浏览器需要重新计算元素及其子元素的位置和大小,并可能...
在CSS3动画中,常见的性能瓶颈是元素的repaint(重绘)和reflow(回流)。重绘是指当元素的外观改变,如颜色、透明度变化时,浏览器会重新绘制该元素及其覆盖区域。而回流则更严重,它涉及元素尺寸或位置的改变,...