1、重排、重绘、合成
在进入主题之前先讲讲浏览器渲染过程
-
HTML被HTML解析器解析成DOM Tree
-
接受CSS文本,将CSS文本转换成浏览器可以理解得结构——styleSheets,转换样式表 中的属性值,使其标准化,计算出DOM树中每个节点的具体样式。
注意:
在给DOM树中每个节点计算具体的样式时要遵循CSS的继承规则和层叠规则
-
DOM树和CSSOM树解析完成之后被合成到一起,形成渲染树。
-
对节点进行布局计算,这个过程叫做重排,即根据渲染树计算每个节点的几何信息。
-
绘制页面即重绘,根据生成的渲染树去绘制整个页面
在渲染过程中重排和重绘是最耗时的过程, 一旦触发重排,我们对DOM的修改引发了DOM几何元素的变化,渲染树需要重新计算, 而重绘只会改变 背景色、文字颜色等属性样式导致样式发生变化, 使浏览器需要根据新的属性进行绘制。更比而言,重排会产生比重绘更大的开销 ,同时这也说明了一点 重绘不一定导致重排,但重排一定会导致重绘。
1.1、重排
所谓重排就是根据渲染树中每个渲染对象的信息,计算出每个渲染对象相应的几何信息(DOM对象的位置和尺寸大小),并将其放置在对应的位置上。这里要稍微提醒下,浏览器默认的布局是流式布局,也就是说如果你更改了某一个DOM节点的信息(这里信息是指会触发重排的)就需要对DOM结构进行重新计算,重新布局界面,以此同时对这个DOM节点的改变还会影响到周边的DOM,可能是全局范围也有可能是局部范围,全局范围是指从根节点开始对整个渲染树重新进行布局,例如当我们改变浏览器窗口的尺寸,或者更改了根节点某些样式,局部范围也就是指影响了某一个分支或者某一部分。
引发重排的操作:
- 页面第一次渲染
- 触发CSS伪类
- 里澜起窗口尺寸发生变化
- 元素位置、尺寸、字体发生变化
- 增加或者删除可见的元素
- 设置style属性
引发重排常见的属性和方法
width height margin padding display border position
overflow clientWidth clientHeight offsetWidth offsetHeight scrollTop scrollLeft
scrollIntoView() scrollTo() getComputedStyle ()
1.2、重绘
重绘就是指当页面中元素样式发生变化时不影响他在文档流中的位置,例如改变元素的背景颜色,字体颜色,浏览器并不会再去重新计算生成渲染树,他只会将新的样式赋给元素然后绘制到页面上。
触发重绘的属性:
color border-style visibility background text-decoration background-image background-position background-repeat box-shadow
1.3、合成
相较于重排和重绘,合成操作的路径就显得非常短了,并不需要触发布局和绘制两个阶段,如果采用了 GPU,那么合成的效率会非常高。所以,关于渲染引擎生成一帧图像的几种方式,按照效率我们推荐合成方式优先,若实在不能满足需求,那么就再退后一步使用重绘或者重排的方式。下面主要讲讲合成的几个重要机制。
1.3.1、分层
分层就是将一个完整的页面分成很多图片,将图片叠加在一起就形成了我们的页面,每个图片就是一个图层,那为什么要引入分层呢?我们试想一下,当我们的页面非常复杂时,有的页面里要实现一些复杂的动画效果,比如点击菜单时弹出菜单的动画特效,滚动鼠标滚轮时页面滚动的动画效果,如果没有采用分层机制,从布局树直接生成目标图片的话,那么每次页面有很小的变化时,都会触发重排或者重绘机制,这会严重影响页面的渲染效率。所以就出现了现在的分层机制,当我们相对某个页面进行操作时,合成器只需要将页面对应的图层进行相应的变化操作就可以了,这样效率就明显得到提高。
注意点:
合成操作是在合成线程上完成的,这也就意味着在执行合成操作时,是不会影响到主线程执行的。这就是为什么经常主线程卡住了,但是 CSS 动画依然能执行的原因。
1.3.2、分块
如果说分层是从宏观上提升了渲染效率,那么分块则是从微观层面提升了渲染效率。通常情况下,页面的内容都要比屏幕大得多,这里将百度的页面作为参考。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WEIRtgp9-1604801915872)(C:\Users\1\Pictures\Screenshots\新建文件夹\QQ截图20201108095303.png)]
从这里我们明显就能看出百度的页面比我们的视口大得多,在显示这个页面时如果等待所有的图层都生成完毕,再进行合成的话,会产生一些不必要的开销,也会让合成图片的时间变得更久。因此,合成线程会将每个图层分割为大小固定的图块,然后优先绘制靠近视口的图块,这样就可以大大加速页面的显示速度。
1.4、性能优化
-
减少DOM操作
- 缓存访问DOM的样式信息,避免过度触发回流
- 如果想多次访问一个DOM,可以先缓存它
-
采用更优的API
- 用querySelectorAll()代替getElementBy…()
- 开启动画的GPU加速
- 用时间委托来减少事件处理器的数量
-
减少重排
-
使用 class 操作样式,而不是频繁操作 style
-
避免使用 table 布局 ,因为table某个元素出发reflow,纳闷整个table元素就会出发reflow
-
批量dom 操作,例如 createDocumentFragment,或者使用框架,例如 React
-
Debounce window resize 事件
-
将position属性设置为absolute或fixed, 这样的元素重排开销比较小,不用考虑它对其他元素的影响
-
对 dom 属性的读写要分离
div.style.top = "10px"; div.style.bottom = "10px"; div.style.right = "10px"; div.style.left = "10px"; console.log(div.offsetWidth); console.log(div.offseHeight); console.log(div.offsetRight); console.log(div.offsetLeft); //在这里只会进行一次重排,如果在输出时去修改样式那么每输出一次就会重排一次
-
will-change: transform 做优化
这会提前告诉眼修改的元素将要做几何变换和透明度变换操作,这时候渲染引擎会将该元素单独实现一图层,等这些变换发生时,渲染引擎会通过合成线程直接去处理变换,这些变换并没有涉及到主线程,这样就大大提升了渲染的效率。
-
-
css优化
- 减少使用js来修改元素样式。尽量用修改class名的方式操作样式
- 动画尽量使用在绝望对点位或固定定位的元素上