一、浏览器的绘制过程
- 浏览器首先将获取得到的HTML文档解析为DOM树,HTML中的每一个标签都是DOM树中的一个节点。DOM树里包含了所有的HTML标签(包括
display: none
隐藏的标签、JS动态添加的标签) - 浏览器把所有的样式解析为CSSOM样式结构体,解析过程中会去掉当前浏览器不能识别的样式
- DOM和CSSOM组合后,构建Render Tree(呈现树),Render Tree类似于DOM树,但是Render Tree能够识别样式,每个Node都有自己的style,并且Render Tree不会包含隐藏的节点(
display:none
这种不占据空间的节点,而非visiable: hidden
这类占据空间的节点),仅仅会包含用于呈现的节点。树中的每个节点,都可以称为Box - 浏览器根据Render Tree来绘制页面
二、reflow(回流)和repaint(重绘)
1、当render tree中有元素改变了尺寸、大小、布局等会影响到其他元素布局的属性时,render tree就需要重新构建,而重新构建的这一个过程,便称为reflow
,reflow
时,浏览器会使得受影响的部分失效,并重新进行构建,完成reflow
后,浏览器重新绘制受影响的部分到屏幕中
2、如果render tree中的一些属性,仅仅是发生元素外观、风格上的改变,而不造成布局上的改变的时候,就只会发生repaint。如:color
、background-color
之类的改变
注意: reflow必将引起repaint,而repaint不一定会引起reflow
三、发生reflow的时机
1、添加、删除可见DOM元素
2、元素位置改变
3、元素尺寸改变:margin、padding、border、width、height
4、内容改变:如img
的width、height变化
5、页面渲染初始化
6、浏览器窗口改变
示例:
var b = document.body.style;
b.padding = "10px"; // 发生reflow+repaint
b.border = "1px solid #EEE"; // 发生reflow+repaint
b.color = "#F00"; // 发生repaint
document.body.appendChild(document.createTextNode('Hello, world!'));
// reflow+repaint
四、浏览器的优化
1、浏览器为了避免每次做一次DOM布局的改变就引起一次reflow从而造成巨大的资源消耗,使用一个队列来延迟执行。浏览器会把那些可能引起reflow、repaint的操作加入队列中,等队列中有一定的元素数量或者到了某一时间间隔后,就flush队列,从而使得多次的reflow/repaint成为一次reflow/repaint
2、但是,有时候我们需要获取一些信息的时候,浏览器不得不进行flush,以确保我们获得的是准确的值,如获取以下值的时候:
- offsetTop、offsetLeft、offsetWidth、offetHeight
- scrollTop、scrollLeft、scrollWidth、scrollHeight
- clientTop、clientLeft、clientWidth、clientHeight
- width、height
- 调用
getComputedStyle()
或者IE的currentStyle
五、如何减少reflow、repaint
1、直接改变className。或者使用cssText
来一次性改变多个属性,如:
e.style.cssText = "width: 100px; height: 100px";
e.className = "someClassName";
2、让要操作的元素进行离线处理
,处理完后一起更新
- 使用DocumentFragment进行缓存操作,引发一次reflow和repaint
display: none
,引发两次reflow和repaint- 使用
cloneNode
和replaceChild
,引发一次reflow和repaint
3、不要经常访问会引起flush队列的属性(即不要经常访问offsetTop这类的属性)
4、让元素脱离动画流,减少回流的Render Tree规模