# 回流
# 什么是回流
大部分情况下页面生成一个新的帧都是 js
通过修改 DOM
或 CSSOM
来触发的,还有一部分是 css
触发的。如果在计算样式的阶段发现页面的布局结构也就是 DOM
树有被修改,那么就会触发回流(重排),然后触发后续渲染流水线的一系列操作。这个代价是非常大的。
# 为什么会回流
# 回流的触发条件
上面提到当 DOM
树被修改时,就会触发回流 (reflow)。在实际操作中我们使用 display:none
来隐藏元素,元素会从 DOM
树上删除,这就会导致回流的产生而且 display:none
元素的所有后代元素都会跟随着一起被移除
# 重绘
# 什么是重绘
在计算样式的阶段发现页面的结构布局未被改变,仅仅是修改了颜色一类的信息也就是 css
的样式,那么就会跳过布局阶段,直接进入绘制阶段,这个过程就叫重绘。相对于回流,重绘的代价要小许多。** 注意无论是重绘还是回流,都是基于 js
代码操作的情况下才会触发。当我们使用 css
动画实现渐变、变形等操作时是不会触发重绘与回流的。** 因为 css
动画是在合成线程上执行的,和 js
完成的动画效果并不同,这个过程称为合成。
# 为什么会重绘
# 重绘的触发条件
使用类似 visibility:hidden
进行隐藏元素时,其原理就是修改该元素的颜色信息。此时会触发重绘,但应用了 visibility:hidden
的元素仅对该元素本身生效,其后代元素并没有影响还是会显示出来,而且在文档流中元素依然会占据其本身的位置
# 分层合成机制
通常我们的页面组成十分复杂,实现动画的效果时绘制的帧此时要输出的帧如果全都在主线程里生成的话,会导致页面性能急剧下降。为了解决这个问题,所以 Chrome
引入的分层合成机制,合成操作是在合成线程上执行的不会影响主线程的执行
# 分层合成
一个页面通常会被划分成多个图层来进行渲染,类似于 ps
里的项目那样进行分层处理最后显示时合并成一个图层。页面元素被分层后,当我们需要对上一帧的某些元素进行几何变换如平移、旋转、缩放等或者阴影、Alpha 渐变、opacity 变化时,合成器只需要将需要变化的图层进行操作并且此时调用的是显卡进行计算处理,所以它的性能会十分高所需合成时间非常短。
# 分块
大多数场景下我们只需要对小部分特定元素做动画效果处理,若我们因为这一小部分元素的动画而进行整个页面的图层合成的话那么明显有许多重复性的无效果的计算。为了减小这部分开销 Chrome
里合成线程会将每个图层分割成固定大小的图开,然后优先绘制靠近视口的图块,这样就可以大大加速页面的显示速度。
但即便如此合成也需要耗费不少时间,于是 Chrome
又会在首次合成图块的时候使用一个低分辨率的图片显示出来,合成器继续绘制正常分辨率的图片绘制完替换掉低分辨率的图片,尽管用户刚刚开始看到的图片分辨率低的内容但也比白屏要好上很多
当我们需要对某个元素做几何变换或者阴影、不透明度变化时,如果使用 js
实现就会牵涉到渲染流水线,而渲染流水线是在主线程里执行的,所以效率低下。但使用 css
实现这些动画效果则是在合成线程上实现的,不会阻塞主线程所以效率会大大提高
我们可以使用 will-change
来明确告诉渲染引擎你会对该元素做一些特效变换
.box { | |
will-change:transform opacity; | |
} |
这段代码就是提前告诉渲染引擎 box
元素将要做几何变换和不透明度操作,渲染引擎会将该元素单独实现一帧,当变换发生时渲染引擎会通过合成线程直接处理变换。
当图层内容发生变化时 (如文字内容改变) 一定会触发重绘或回流,合成只是基于图层内容不变的情况下才产生!