什么是回流(reflow)与重绘(repaint)?要回答这个问题,得先认识一下浏览器原理,最主要的是要了解浏览器如何渲染网页。
浏览器解析的大概工作流
当浏览器接收到 HTML 时,就会解析它,将其分解为浏览器所能理解的词汇,而这个词汇由于 HTML5 DOM 规范定义,在所有浏览器中是保持一致的。然后浏览器通过一系列步骤来构造和渲染页面。如下概述:
- 使用 HTML 创建文档对象模型(DOM)
- 使用 CSS 创建 CSS 对象模型(CSSOM)
- 基于 DOM 和 CSSOM 执行脚本(Script)
- 合并 DOM 和 CSSOM 形成渲染树(Render Tree)
- 使用渲染树布局(Layout)所有元素的大小和位置
- 绘制(Paint)所有元素
如下图所示:
可以看到 Reflow 和 Repain 分别出现在了第五和第六步。因此我们给出下面的定义:
Reflow
对于每个 DOM 结构中的各个元素都有自己的盒子(模型),这些都需要浏览器根据各种样式(浏览器的,开发人员定义的)来计算并根据计算结果将元素放到它出现的位置,这个过程称为 Reflow。
Repaint
当各种盒子的位置、大小以及其他属性,例如颜色、字体大小都确定下来后,浏览器便把这些元素都按照各自的特性绘制了一遍,于是页面的内容出现了,这个过程称之为 Repaint。
可见这两个过程都会影响性能的,因此我们需要了解一些常见的会引起 repaint 和 reflow 的一些操作,并且应该尽量减少以提高渲染速度。
引起 Repaint 和 Reflow 的一些操作
Reflow 的成本比 Repaint 的成本高得多的多。DOM Tree 里的每个结点都会有 Reflow 方法,一个结点的 Reflow 很有可能导致子结点,甚至父点以及同级结点的 Reflow。在一些高性能的电脑上也许还没什么,但是如果 Reflow 发生在手机上,那么这个过程是非常痛苦和耗电的。所以,下面这些动作有很大可能会是成本比较高的。
- 增加、删除、修改 DOM 结点时,会导致 Reflow 或 Repaint。
- 移动 DOM 的位置,或是搞个动画的时候。
- 修改 CSS 样式的时候。
- Resize 窗口的时候(移动端没有这个问题),或是滚动的时候。
- 当你修改网页的默认字体时。
注意:display:none 会触发 Reflow,而 visibility:hidden 只会触发 Repaint,因为没有发现位置变化。
如何优化?
Reflow 是不可避免的,只能将 Reflow 对性能的影响减到最小。下面给出几条建议:
- 避免逐项更改样式。最好一次性更改 style 属性,或者将样式列表定义为 class 并一次性更改 class 属性。
- 把 DOM 离线后修改:
- 使用 documentFragment 对象在内存里操作 DOM。
- 先把 DOM 给 display:none (触发一次 Repaint),然后你想怎么改就怎么改。比如修改 100 次,然后再把他显示出来。
- clone 一个 DOM 节点到内存里,然后想怎么改就怎么改,改完后,和在线的那个的交换一下。
- 不要把 DOM 节点的属性值放在一个循环里当成循环里的变量。不然这会导致大量地读写这个结点的属性。
- 尽可能的修改层级比较低的 DOM 节点。
- 将复杂的元素绝对定位或固定定位,使它脱离文档流。
- 尽量不要使用 table 布局。因为可能很小的一个小改动会造成整个 table 的重新布局。
认识了一些基本的浏览器的原理后,是不是更有信心了呢?有一天产品要你自定义一个滑条取代浏览器自带的滑条以获得一致外观。我们可以这样回答:自定义滑条会引起 Reflow 导致性能问题,即使 IScoll 这样成熟的方案也没解决这一问题,因为还是使用自带滑条为好。
评论 (0)