浏览器的渲染过程
- 解析HTML文件,生成DOM树,解析css文件,生成CSSOM树
- 将DOM树和CSSOM树合并,生成Render Tree(渲染树)
- Layout(回流):根据生成的渲染树,确定节点的几何信息(尺寸和位置)
- Painting(重绘):根据渲染树已经回流得到的几何信息,确定节点的绝对像素(包括颜色等等)
- Display:将像素发送给CPU,展示在页面。
CSSOM(CSS Object Modal):CSS对象模型,是树形式的所有CSS选择器及每个选择器相关属性的映射。它允许用户动态读取和修改 CSS样式
生成渲染树
注意:渲染树只包含可见节点,如 display: none ,这种节点,不会出现在渲染树
回流/重排(Reflow)
当DOM的变化,引发了元素的几何信息(如位置,尺寸)变更,浏览器需要重新计算元素的几何信息,此过程称为重排。
引发重排的场景
- 页面初始化渲染,必然引发重排,且是消耗最大的
- 删除/添加DOM元素
- 元素尺寸发生改变
- 元素位置发生改变
- 元素内部文字,字体大小发生改变
- 元素内容发生改变,如文字增加,或变成不同尺寸的图片
- 浏览器视口发生改变
- 查询某些属性或者调用某些方法,如offsetWidth,getComputedStyle等,会触发,因为需要“即时性”。
重排的影响范围
浏览器渲染界面是基于流式布局的,所以触发重排的时候,会影响到其周围的元素。影响的范围有两种
全局范围:从根节点开始,对整个渲染树进行重新布局 局部:对渲染树的某部分或某个渲染对象进行重新布局
局部范围重排: 如果,将某个DOM的几何信息,完全固定,然后在dom的内部,进行重排。不会影响外部元素。
重绘(Painting)
当一个元素的外观发生改变,但没有改变布局时,重新将元素外观绘制出来,此过程称为重绘。
重排与重绘
重绘:某些元素的外观发生改变
重排:重新生成布局,重新排列元素。
如上,仅改变元素的外观时,一般不会引起元素布局的变更,即不会导致重排。但,如果浏览器完成重排,需要重新绘制此次重排受影响的部分。 故,重排必导致重绘,重绘不一定导致重排
重排优化
可知,重排是非常消耗性能的,且会严重影响到用户体验。故,我们需要尽量减少重排
浏览器的优化机制
大多数浏览器,会通过队列化修改并批量执行,以此来优化,减少重排次数。 浏览器会将修改操作放在队列之中,达到阈值之后,进行重排,清空队列。 但是,获取布局信息的操作,会强制队列刷新
| offsetTop | offsetLeft | offsetHeight | offsetWidth |
| scrollTop | scrollLeft | scrollWidth | scrollHeight |
| clientTop | clientLeft | clientWidth | clientHeight |
| getcomputedStyle() | getBoundingClientRect() |
具体可参考gist.github.com/paulirish/5…
减少重绘和重排
1、减少重绘重排次数,样式集中改变
const el = document.getElementById('test') el.style.padding = '5px' el.style.borderLeft= '1px' el.style.borderRight = '2px' 例中,有三个样式属性被改变,每一个都会影响元素的几何属性,引起重排。 以上,浏览器对其做了优化,只会触发重排。 如果是在旧版的浏览器,会导致三次重排。
所以,我们可以采取以下的方式: 使用cssText
const el = document.getElementById('test')
el.style.cssText += 'border-left: 1px; border-right: 2px; padding: 5px;'
使用className
const el = document.getElementById('test')
el.className += 'active'
2、分离读写操作
当我们访问元素的一些属性时,会导致浏览器强制清空队列,进行强制同步布局。 举个例子
const el = document.getelementById('test')
el.style.left = el.offsetLeft + 1 + 'px'
el.style.top = el.offsetTop + 1 + 'px'
如上,同步地读、写元素的几何信息,会导致浏览器重复重排
const el = document.getElementById('text')
const left = el.offsetLeft
const top = el.offsetTop
el.style.left = left.+ 1 + 'px'
el.style.top = top + 1 + 'px'
我们修改为如上的先读后写的方式,只会触发一次重排
3、批量修改DOM时
如果,我们要对某一个节点进行反复修改,我们可以先让该节点脱离文档流,执行修改之后,再插入文档流
- 使用display: none
- 使用documentFragment创建一个dom碎片,在其上批量操作dom
- 复制节点,在副本上操作,然后替换原节点 注:因为浏览器的队列合并处理,以上方法在现代浏览器中,收效甚微
4、绝对定位脱离文档流
如果有某个元素,其几何信息会频繁地发生变更,比如动画。 可以让它脱离文档流,绝对定位。 这样,它的重排仅出现在局部,不会导致父元素及邻近元素的频繁重排
5、css3硬件加速(GPU加速)
使用css3硬件加速,可以让 transform、opacity、filters这些动画不引起回流重绘。但是对于动画的其他属性,如background-color,会引起回流重绘,不过它同时还可以提升这些动画的性能。
总结
渲染的三个阶段Layout,Paint,Composite Layers。Layout:重排,又叫回流。Paint:重绘,重排重绘都是在CPU中发生的。 Composite Layers:CPU把生成的BitMap(位图)传输到GPU,渲染到屏幕。