“开发者工具”的“Layers”标签,选择“document”层,来实际体验下绘制列表,如下图所示:
在该图中,区域 1 就是 document 的绘制列表,拖动区域 2 中的进度条可以重现列表的绘制过程。
从 HTML 到 DOM、样式计算、布局、图 层、绘制、光栅化、合成和显示。
完整的渲染流程大致可总结:
- 渲染进程将 HTML 内容转换为能够读懂的DOM 树结构
- 渲染引擎将 CSS 样式表转化为浏览器可以理解的styleSheets,计算出 DOM 节点的样式
- 创建布局树,并计算元素的布局信息
- 对布局树进行分层,并生成分层树
- 为每个图层生成绘制列表,并将其提交到合成线程
- 合成线程将图层分成图块,并在光栅化线程池中将图块转换成位图
- 合成线程发送绘制图块命令DrawQuad给浏览器进程
- 浏览器进程根据 DrawQuad 消息生成页面,并显示到显示器上
“重排”“重绘”和“合成
如果你通过 JavaScript 或者 CSS 修改元素的几何位置属性,例如改变元素的宽度、高度等,那么浏览器会触发重新布局,解析之后的一系列子阶段,这个过程就叫 重排。无疑,重排需要更新完整的渲染流水线,所以开销也是最大的。
如果修改了元素的背景颜色,那么布局阶段将不会被执行,因为并没有引 起几何位置的变换,所以就直接进入了绘制阶段,然后执行之后的一系列子阶段,这个过程就叫重绘。相较于重排操作,重绘省去了布局和分层阶段,所以执行效率会比重排操作要高一些。
那如果你更改一个既不要布局也不要绘制的属性,会发生什么变化呢?渲染引擎将跳过布局和绘制,只执行后续的合成操作,我们把这个过程叫做合成。
使用了 CSS 的 transform 来实现动画效果,这可以避开重排和重绘阶段,直接在非主线程上执行合成动画操作。这样的效率是最高的,因为是在非主线程上合成,并没有占用主线程的资源,另外也避开了布局和绘制两个子阶段,所以相对于重绘和重排,合成能大大提升绘制效率。
重排和重绘都是渲染进程的主线程中进行的,合成线程(图中非主线程)属于渲染进程。