上一篇导航流程讲到,网络进程请求到数据回来后,解析响应数据,并让浏览器进程通知渲染进程准备好,准备好后,浏览器向渲染进程发起提交文档
的消息,渲染进程接受文档开始渲染。
渲染流程机制过于复杂,大致为以下渲染流水线
按照渲染的时间顺序,流水线可分为如下几个子阶段:构建 DOM 树、样式计算、布局阶段、分层、绘制、分块、光栅化和合成。
1. 构建DOM树
浏览器无法识别HTML,所以得经过HTML解析器输出为dom树保存在内存结构中。
2. 样式计算
和 HTML 文件一样,浏览器也是无法直接理解这些纯文本的 CSS 样式,所以当渲染引擎接收到 CSS 文本时,会执行一个转换操作,将 CSS 文本转换为浏览器可以理解的结构——styleSheets
该阶段的目的是为了计算出 DOM 节点中每个元素的具体样式,在这个阶段最终输出的内容是每个 DOM 节点的样式,并被保存ComputedStyle 的结构内。
- 样式属性值标准化,2em、blue、bold 类型数值不容易被渲染引擎理解,标准化为,2em 被解析成了 32px,red 被解析成了 rgb(255,0,0),bold 被解析成了 700
- 计算出 DOM 树中每个节点的具体样式,继承规则,层叠规则
3. 布局阶段
- 创建布局树
为了构建布局树,浏览器大体上完成了下面这些工作:
- 遍历 DOM 树中的所有
可见节点
,并把这些节点加到布局树中; - 而不可见的节点会被布局树忽略掉,如 head 标签下面的全部内容,再比如 body.p.span 这个元素,因为它的属性包含 dispaly:none,所以这个元素也没有被包进布局树。
- 布局计算
接下来,就要计算布局树节点的坐标位置了,计算过程非常复杂,我们这里先跳过不讲
4. 分层
页面中有很多复杂的效果,如一些复杂的 3D 变换、页面滚动,或者使用 z-indexing 做 z 轴排序,溢出裁切等,渲染引擎还需要为特定的DOM节点生成专用的图层,并生成一棵对应的图层树(LayerTree)
- 拥有层叠上下文属性的元素会被提升为单独的一层。eg:position:fixed,z-index,opacity 等。
- 需要剪裁(clip)的地方也会被创建为图层。
5. 图层绘制
渲染引擎会把一个图层的绘制拆分成很多小的绘制指令,然后再把这些指令按照顺序组成一个待绘制列表
6. 栅格化(raster)操作 (先划分为图块(tile),再栅格化)
绘制列表只是用来记录绘制顺序和绘制指令的列表,而实际上绘制操作是由渲染引擎中的合成线程
来完成的。
在有些情况下,有的图层可以很大,但是通过视口,用户只能看到页面的很小一部分,所以要绘制出所有图层内容的话,就会产生太大的开销,基于这个原因,合成线程会将图层划分为图块(tile)。
合成线程会按照视口附近的图块来优先生成位图(像素图),渲染进程维护了一个栅格化的线程池,所有的图块栅格化都是在线程池内执行的。
通常,栅格化过程都会使用 GPU 来加速生成,过程叫快速栅格化,生成的位图被保存在 GPU 内存中。GPU 操作是运行在 GPU 进程中,那么最终生成位图的操作是在 GPU 中完成的,这就涉及到了跨进程操作。
渲染进程把生成图块的指令发送GPU,然后在 GPU 中执行生成图块的位图,并保存在 GPU 的内存中。
7. 合成和显示
一旦所有图块都被光栅化,合成线程就会生成一个绘制图块的命令——“DrawQuad”,然后将该命令提交给浏览器进程。
浏览器进程里面有一个叫 viz 的组件,用来接收合成线程发过来的 DrawQuad 命令,然后根据 DrawQuad 命令,将其页面内容绘制到内存中,最后再将内存显示在屏幕上。
整个渲染流程 (发生在渲染进程中,主要分为两个线程)
总结:
- 渲染进程将 HTML 内容转换为能够读懂的 DOM 树结构。
- 渲染引擎将 CSS 样式表转化为浏览器可以理解的 styleSheets,计算出 DOM 节点的样式。
- 创建布局树,并计算元素的布局信息。
- 对布局树进行分层,并生成分层树。
- 为每个图层生成绘制列表,并将其提交到
合成线程
。 - 合成线程将图层分成图块,并在光栅化线程池中将图块转换成位图。
- 合成线程发送绘制图块命令 DrawQuad 给浏览器进程。
- 浏览器进程根据 DrawQuad 消息生成页面,并显示到显示器上。
相关概念
看看三个和渲染流水线相关的概念——“重排”“重绘”和“合成”。
- 重排 (更新了元素的几何属性)
- 可以看出,如果你通过 JavaScript 或者 CSS 修改元素的几何位置属性,例如改变元素的
宽度、高度
等,那么浏览器会触发重新布局,解析之后的一系列子阶段,这个过程就叫重排。无疑,重排需要更新完整的渲染流水线,所以开销也是最大的。 - 重绘(更新元素的绘制属性)eg:背景颜色
- 重绘省去了布局和分层阶段,所以执行效率会比重排操作要高一些。
- 直接合成阶段
- CSS 的 transform 来实现动画效果,这可以避开重排和重绘阶段,
直接在非主线程上执行合成动画操作
。这样的效率是最高的,因为是在非主线程上合成,并没有占用主线程的资源,另外也避开了布局和绘制两个子阶段,所以相对于重绘和重排,合成能大大提升绘制效率。