浏览器页面的渲染流程

2,599 阅读6分钟

在上一篇当中《从输入URL到⻚面展示,这中间发生了什么?》最重要的过程是在页面的渲染。这一篇我们来梳理一下渲染流程。

首先我们先熟悉页面“三剑客”——HTML、CSS、JavaScript。HTML全称是超文本标记语言,俗称标签;CSS全称是层叠样式表,俗称样式;JavaScript是浏览器的脚本语言,使用脚本可以使内容“动”起来,简称JS

熟悉完三剑客后,来看看浏览器的渲染机制。它的过程较于复杂,在执行的过程当中会被划分为多个子阶段,我们把处理一系列子阶段的流程称之为渲染流水线。我们没必要去熟悉每个子阶段,只需了解其中几个重要的子阶段过程:

先是DOM树样式计算、创建布局树、进行分层为分层树、再进入合成线程、形成图块进行光栅化转为位图、最后浏览器进程根据命令DrawQuad生成页面显示出来。

DOM树

对于DOM树相信我们并不陌生,我们常常听到且在浏览器console里面输入document可以打印出来看得到。

DOM数构建过程示意图

样式计算(Recalculate Style)

浏览器不仅需要将HTML转成可以识别的DOM树结构,而且相应的CSS样式也需要转化可以识别的结构stylesheet。其实我们可以在浏览器开发者工具console标签处输入document.styleSheets,就可以看到如下图所示:

至于样式计算还有关于样式属性标准化层叠规则的延展,此处就不做过多输出,感兴趣的话可以搜索加深了解。

布局阶段(Layout)

顾名思义,布局就是浏览器按照DOM树和styleSheets数据结构开始布局计算,正如我们搭建房子,把设计图纸画好后再动手搭建。

分层-图层树(LayerTree)

当以为布局好了就可以渲染页面时,竟然不知浏览器还需做下一步工作,即分层。因为页面中有很多复杂的效果,如一些复杂的3D变换、页面滚动,或者使用z- indexing做z轴排序等,为了更加方便地实现这些效果,**渲染引擎还需要为特定的节点生成专用的图层,并生成一棵对应的图层树(LayerTree)。**好比PS(PhotoShop)软件里面的图层概念,正是这些图层叠加在一起构成 了最终的页面图像。 熟悉了图层,再来看为什么浏览器还不能渲染页面,那是因为图层树需要和布局树结合,也叫合成composition

布局树和图层树关系示意图

结合上图的布局树和图层树的关系示意图,这里帮助笔者疏通了CSS样式当中层叠上下文的知识点。因为拥有层叠上下文属性的标签会被提升为单独的一层

图层绘制(paint) 图层树形成后还需要图层绘制(paint),我们也可以打开浏览器的开发者工具的“layer”标签,选择“document”层,来实际体验下绘制列表,如下图所示:

在图中,区域1就是document的绘制列表,拖动区域2中的进度条可以重现列表的绘制过程。

栅格化(raster)

要明白栅格化,先要理解什么是图块和位图。首先图块(tile)是渲染进程即浏览器内核当中的合成线程将图层划分为大小512x512或者256x256的区块

其次位图则是栅格化的过程:合成线程会按照视口附近的图块来优先生成位图,实际生成位图的操作是由栅格化来执行的,将图块转换为位图

显示(DrawQuad)

一旦所有图块都被光栅化,合成线程就会生成一个绘制图块的命令——“DrawQuad”,然后将该命令提交给浏览器进程处理,最终将页面display呈现出来。

渲染流水线大总结

至此已经分析完整个渲染流程,从HTML到DOM、样式计算、布局、图层、绘制、光栅化、合成和显示。下面一张图是总结下这整个渲染流程:

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

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

当我们把浏览器内核工作流程熟悉后再回过头看性能优化当中常常提到的“重排”、“重绘”和“合成”,相信就可以理解更加透彻一点了。

重排是更新了元素的几何属性,例如改变了元素的宽高,会触发浏览器重新渲染流程,解析之后的一系列子阶段,所以说重排会导致开销最大

重绘是更新元素的绘制属性,例如元素的背景色,不会引起元素的几何位置变化,就直接进入了绘制阶段,然后执行之后的一系列子阶段。所以相对重排来说,执行效率会高一些,因为省去了布局计算和分层阶段。

合成是改了元素的既不布局也不绘制的属性,例如transform实现动画效果,避开重排和重绘阶段,直接在非主线程上 执行合成动画操作。这样的效率是最高的,因为是在非主线程上合成,并没有占用主线程的资源,另外也避开了布局和绘制两个子阶段,所以相对于重绘和重排,合成能大大提升绘制效率。

通过整理来自极客时间课程《浏览器工作原理与实践》笔记,对浏览器的渲染进程了解更加深入,对于页面从URL到展现的过程了然于胸,强迫自己做笔记还是蛮有收获滴~希望笔记可以帮助到大家。