A blog by derek

前端页面之重绘与回流

October 11, 2014

浏览器的渲染机制

  • 浏览器在获取到后端传过来的内容后,就开始执行render流程。不同内核的浏览器render流程略有不同,大致的工作流程:

    • 解析HTML(HTML Parser)
    • 构建DOM树(DOM Tree)
    • 渲染树构建(Render Tree):渲染树和DOM树比较相似,但渲染树能够根据style进行调整
    • 绘制渲染树(Painting):渲染树构建、布局及绘制

Reflow

  • Reflow 「回流」,在某一个 DOM 元素的位置发生改变后触发,而且它会重新计算所有元素的位置和在页面中的占有的面积,会引起页面某一个部分甚至整个页面的重新渲染。改变某一个元素会影响它所有的子节点 (children)、祖先节点 (ancestors) 及兄弟节点(siblings)。

Repaint

  • Repaint 「重绘」,绘是一个元素外观的改变所触发的浏览器行为。例如改变在opacity,background-color,visibility和outline等都会触发,浏览器会根据元素的新属性重新绘制,使元素呈现新的外观。重绘不会带来重新布局,并不一定伴随回流。「重绘」的开销还是比较昂贵的,因为浏览器会在某一个 DOM 元素的视觉效果改变后去 check 这个 DOM 元素内的所有节点。回流是更明显的一种改变,可以理解为渲染树需要重新计算。

重绘(Repaint)不一定会引起回流(Reflow回流),但回流必将引起重绘(Repaint)。


触发Reflow回流的原因

  • 页面渲染初始化
  • 浏览器窗口尺寸改变——resize事件发生时
  • 元素尺寸改变——边距、填充、边框、宽度和高度
  • 增加或者移除样式表
  • 内容变化——比如文本改变或者图片大小改变而引起的计算值宽度和高度改变
  • 激活CSS伪类
  • JavaScript操作DOM
  • 设置style属性的值
  • CSS3 Animation或Transition

触发浏览器的Repaint和Reflow的原因

  • 页面首次加载
  • DOM元素添加、修改(内容)和删除(Reflow + Repaint)
  • 仅修改DOM元素的颜色(只有Repaint,因为不需要调整布局)
  • 应用新的样式或修改任何影响元素外观的属性
  • Resize浏览器窗口和滚动页面
  • 读取元素的某些属性(offsetLeft、offsetTop、offsetHeight、offsetWidth、getComputedStyle())

reflow的开销大于repaint,所以用marginLeft, width ,height等属性改变dom时我们要注意减少影响的范围。基本原则就是,把动画元素用position:absolute踢出文档流,这样就限制在了absolute元素的子节点。

通常在文档初次加载时,浏览器引擎会解析HTML文档来构建DOM树,之后根据DOM元素的几何属性构建一棵用于渲染的树。渲染树的每个节点都有大小和边距等属性,类似于盒子模型。当渲染树构建完成后,浏览器就可以将元素放置到正确的位置了,再根据渲染树节点的样式属性绘制出页面。由于浏览器的流布局,对渲染树的计算通常只需要遍历一次就可以完成。

  1. DOM元素的几何属性变化。 当DOM元素的几何属性变化时,渲染树中的相关节点就会失效,浏览器会根据DOM元素的变化重建构建渲染树中失效的节点。之后,会根据新的渲染树重新绘制这部分页面。而且,当前元素的回流也许会带来相关元素的回流。 2.DOM树的结构变化。 当DOM树的结构变化时,例如节点的增减、移动等,也会触发回流。浏览器引擎布局的过程,类似于树的前序遍历,是一个从上到下从左到右的过程。通常在这个过程中,当前元素不会再影响其前面已经遍历过的元素。所以,如果在body最前面插入一个元素,会导致整个文档的重新渲染,而在其后插入一个元素,则不会影响到前面的元素。

减少重绘和回流的解决方案:

  • 将多次改变样式属性的操作合并成一次操作
  • 使元素脱离文档流。将需要多次回流的元素,position属性设为absolute或fixed,这样此元素就脱离了文档流,它的变化不会影响到其他元素。例如有动画效果的元素就最好设置为绝对定位。
  • 在内存中多次操作节点,完成后再添加到文档中去。例如要异步获取表格数据,渲染到页面。可以先取得数据后在内存中构建整个表格的html片段,再一次性添加到文档中去,而不是循环添加每一行。
  • 由于display属性为none的元素不在渲染树中,对隐藏的元素操作不会引发其他元素的回流。如果要对一个元素进行复杂的操作时,可以先隐藏它,操作完成后再显示。这样只在隐藏和显示时触发2次回流。
  • 在需要经常取那些引起浏览器回流的属性值时,要缓存到变量。

页面上的回流(reflow)减到最小

  1. 减少不必要的DOM深度。因为无论你改变DOM节点树上任何一个层级都会影响节点树的每个层级——从根结点一直到修改的子节点。不必要的节点深度将导致执行回流时花费更多的时间。
  2. 精简css,去除没有用处的css
  3. 如果你想让复杂的表现发生改变,例如动画效果,那么请在这个流动线之外实现它。使用position-absolute或position-fixed来实现它。
  4. 避免不必要的复杂的css选择符,尤其是使用子选择器,或消耗更多的CPU去做选择器匹配。
  5. 无线性能优化:Composite
  6. web性能优化之--reflow和repaint

推荐一个测试工具:DynaTrace,通过它你可以清楚看到页面是在何时reflow、何时repaint,分别占用了多长时间,也还可以看到用户的事件,如click,mouse相关事件,keyboard事件等。当然这个工具提供的功能远不止于此,你可以深入的研究下,结合其它的性能测试工具来对你的网站做一个全面的性能分析。


Derek

Personal blog by Derek. Reach your own conclusions