前言:
在了解回流与重绘之前,我们先来了解下浏览器的渲染机制:
1. 浏览器采用的是流式布局模型(Flow Based Layout)
2. 浏览器会把CSS解析成CSSOM Tree,把HTML解析成 DOM Tree,把这两个合并成 Render Tree
3. 有了Render Tree 我们就知道了所有节点的位置和样式,浏览器就开始计算他们在页面中的位置,然后开始绘制
4. 由于浏览器是流式布局,对于Render Tree的计算通常只需要遍历一遍就可以完成。但是table及其内部的元素除外,他们可能要计算多次,需要花费等同的元素3倍的时间,这也是不推荐使用table的原因。
回流(Reflow)
当Render Tree 中的部分或全部元素的尺寸,结构或者处罚某些属性时,浏览器会重新计算并渲染页面,称为回流。此时浏览器需要重新进行计算,计算后还需要重新页面布局,因此是较重的操作。
会导致回流的操作有:
- 页面初次渲染
- 浏览器窗口发生改变
- 元素尺寸,位置,内容发生变化
- 元素字体大小变化
- 添加或者删除的可见dom元素
- 激活CSS伪类,例如 :hover
- 查询某些属性或调用某些方法
一些常用的会导致回流的属性或方法
- clientWidth, clientHeight, clientTop, clientLeft
- offsetWidth, offsetHeight, offsetTop, offsetLeft
- scrollWidth, scrollHeight, scrollTop, scrollLeft
- scrollIntorView(), scrollInToViewIfNeeded()
- getComputedStyle()
- getBoundingClientRect()
- scrollTop
- ...
重绘(Repaint)
当元素的样式改变不影响布局时(例如:color, background-color, visibility等),浏览器将使用重绘对元素进行更新,此时由于只需要对UI层面重新绘制,因此损耗较少
回流一定会导致重绘,但是重绘不一定会导致回流
如何避免:
CSS
- 避免使用table布局
- 尽可能在DOM树的最末端改变class
- 避免设置多层内联样式
- 避免CSS表达式
JavaScript
- 避免频繁的操作样式,最好一次性写好style属性,或者将样式定义为calss,并一次性更改class属性
- 避免频繁操作DOM,创建一个 documentFragment ,这这个上面应用所有的DOM操作,最后再把它添加到文档中
- 也可以先设置为display:none; 操作结束后再把它显示出来,因为在dispkay属性为none的元素上进行DOM操作不会引起回流和重绘
- 避免频繁读取引发回流、重绘的属性,如果要多次使用,建议先把它缓存起来
- 对具有复杂动画的元素使用绝对定位,使它脱离文档流,否则会引起父元素及后续元素频繁回流。