浏览器的渲染进程工作过程

1,565 阅读6分钟
从一个问题开始,重排和重绘哪个消耗的性能更大?

我想很多人只要刷过面经的,应该都可以答得上来最简单的答案:重排消耗的性能大。可是,然后呢?为什么是这个答案呢?

那接下就有我来再带大家去深入的了解下。

渲染过程介绍

按照渲染的时间顺序,流水线可分为如下几个子阶段:构建 DOM 树、样式计算、布局阶段、分层、绘制、分块、光栅化和合成。

在每个阶段里,我们可以看看,它们都输入了哪些内容,处理的过程以及最后的输出内容

构建dom树

为什么要构建 DOM 树呢?这是因为浏览器无法直接理解和使用 HTML,所以需要将 HTML 转换为浏览器能够理解的结构——DOM 树。

构建 DOM 树的输入内容是一个非常简单的 HTML 文件,然后经由 HTML 解析器解析最终输出树状结构的 DOM。

样式计算

当渲染引擎接收到 CSS 文本时,会执行一个转换操作,将 CSS 文本转换为浏览器可以理解的结构——styleSheets。然后,对styleSheets进行标准化处理。

什么是标准化处理呢,就是将所有的属性值转换为渲染引擎容易理解的、标准化的计算值。如下图:

image.png

在接下来,计算DOM树节点的每个样式属性。

这涉及到CSS的继承和层叠规则。继承简单来说就是子元素会获得父元素上面的样式。我们打开浏览器的开发者工具的时候,就能看到,比如一个class名,往下走可以看到继承的各种属性。

image.png

所以,在样式计算阶段,输入的CSS文档内容,然后经由类似的css解析器解析,将styleSheets输出。

布局阶段

Chrome 在布局阶段需要完成两个任务:创建布局树和布局计算。

创建布局

为了构建布局树,浏览器大体上完成了下面这些工作:

  • 遍历 DOM 树中的所有可见节点,并把这些节点加到布局树中;
  • 而不可见的节点会被布局树忽略掉,如 head 标签下面的全部内容,再比如 body.p.span 这个元素,因为它的属性包含 dispaly:none,所以这个元素也没有被包进布局树。

布局计算

通过js和css,计算每个dom元素在页面上的位置。

分层

接下来,渲染引擎会根据节点是否特殊,来生成不同的图层。比如复杂的3D转换,还有页面滚动,还有Z-index的,都会有一个特定的图层。

渲染引擎给页面分了很多图层,这些图层按照一定顺序叠加在一起,就形成了最终的页面。

图层绘制,栅格化以及合成显示

对每个图层进行单独的绘制,就像画画一样。

合成线程会按照视口附近的图块来优先生成位图,实际生成位图的操作是由栅格化来执行的。所谓栅格化,是指将图块转换为位图。渲染进程维护了一个栅格化的线程池,所有的图块栅格化都是在线程池内执行的。栅格化过程都会使用 GPU 来加速生成,使用 GPU 生成位图的过程叫快速栅格化,或者 GPU 栅格化,生成的位图被保存在 GPU 内存中。

一旦所有图块都被光栅化,合成线程就会生成一个绘制图块的命令——“DrawQuad”,然后将该命令提交给浏览器进程。浏览器进程里面有一个叫 viz 的组件,用来接收合成线程发过来的 DrawQuad 命令,然后根据 DrawQuad 命令,将其页面内容绘制到内存中,最后再将内存显示在屏幕上。

渲染引擎总结:

image.png

结合上图,一个完整的渲染流程大致可总结为如下:

  • 渲染进程将 HTML 内容转换为能够读懂的 DOM 树结构。
  • 渲染引擎将 CSS 样式表转化为浏览器可以理解的 styleSheets,计算出 DOM 节点的样式。
  • 创建布局树,并计算元素的布局信息。对布局树进行分层,并生成分层树。
  • 为每个图层生成绘制列表,并将其提交到合成线程。
  • 合成线程将图层分成图块,并在光栅化线程池中将图块转换成位图。
  • 合成线程发送绘制图块命令 DrawQuad 给浏览器进程。
  • 浏览器进程根据 DrawQuad 消息生成页面,并显示到显示器上。

重排和重绘

看了上面的渲染流程后,现在就可以说说重排和重绘是发生在什么时候了。

重排:你通过 JavaScript 或者 CSS 修改元素的几何位置属性,例如改变元素的宽度、高度等,那么浏览器会触发重新布局,解析之后的一系列子阶段,这个过程就叫重排。无疑,重排需要更新完整的渲染流水线,所以开销也是最大的。

重绘:修改了元素的背景颜色,那么布局阶段将不会被执行,因为并没有引起几何位置的变换,所以就直接进入了绘制阶段,然后执行之后的一系列子阶段,这个过程就叫重绘。相较于重排操作,重绘省去了布局和分层阶段,所以执行效率会比重排操作要高一些。

合成:我们使用了 CSS 的 transform 来实现动画效果,这可以避开重排和重绘阶段,直接在非主线程上执行合成动画操作。这样的效率是最高的,因为是在非主线程上合成,并没有占用主线程的资源,另外也避开了布局和绘制两个子阶段,所以相对于重绘和重排,合成能大大提升绘制效率。 通过这个,我们也就知道页面的性能优化的方向在哪里了。

关于浏览器在渲染之前的工作过程,可以看的我上一篇文章。

从一道面试题开始:浏览器从输入URL到渲染页面的过程

感谢


谢谢你读完本篇文章,希望对你能有所帮助,如有问题欢迎各位指正。

我是南瓜小神(✿◡‿◡),如果觉得写得可以的话,请点个赞吧❤。