本文已参与掘金创作者训练营第三期「话题写作」赛道,详情查看:掘力计划|创作者训练营第三期正在进行,「写」出个人影响力。
浏览器有浏览器进程、渲染进程、网络进程、GPU进程等,用户在浏览器地址栏输入一个URL后,在跳转到新页面前,一般会经过这样的流程:浏览器进程通过进程通信把URL发送给网络进程,然后网络进程会进行一系列的操作后(查找缓存、DNS解析、建立TCP连接、发送请求、解析响应头等等),获得了响应数据,并告诉浏览器进程;然后浏览器进程通知渲染进程,接到通知后,渲染进程便开始和网络进程建立传输管道,接收来自网络进程的响应数据。
接收完数据后,渲染进程便着手开始渲染操作——把HTML、CSS、JavaScript等资源渲染成页面。按照渲染的时间顺序,流水线可分为如下几个子阶段:构建 DOM 树、样式计算、布局阶段、分层、绘制、分块、光栅化和合成。
- 构建DOM树 为什么要构建 DOM 树呢?这是因为浏览器无法直接理解和使用 HTML,所以需要将 HTML 转换为浏览器能够理解的结构——DOM 树。
如上图的html代码,最终会在浏览器生成这样的dom数:
这一步浏览器已经生成了DOM树,接下来就是要让 DOM 节点拥有正确的样式,这就需要样式计算了。
- 样式计算
从图中可以看出,CSS 样式来源主要有三种方式:通过 link 引用的外部CSS样式文件、通过style标签内嵌的CSS样式以及通过style属性内联的CSS样式。 和HTML文件一样,浏览器也无法直接理解这些纯文本的CSS样式代码,所以当渲染引擎接收到 CSS文本时,会执行一个转换操作,将 CSS 文本转换为浏览器可以理解的结构——styleSheets。 具体什么是styleSheets,我们可在控制台通过执行document.styleSheets查看。
浏览器把CSS文本转化为它自身可以理解的结构后,那么接下来就要对CSS进行属性值的标准化操作。那什么是标准化呢,
html {font-size: 14px }
body { font-size: 2em }
p {color:red;}
div {font-weight: bold}
可以看到上面的CSS文本中有很多属性值,如 2em、red、bold,这些类型数值不容易被渲染引擎理解,所以需要将所有值转换为渲染引擎容易理解的、标准化的计算值,这个过程就是属性值标准化。 标准化后的CSS属性值如下所示:
html {font-size: 14px }
body { font-size: 28px }
p {color: rgb(255,0,0);}
div {font-weight: 700}
有了这些标准化的CSS后,浏览器就可以计算出每个dom节点的具体样式了截下来就进入了布局阶段。
- 布局阶段 现在,浏览器已有了DOM树和DOM树中元素的样式,但这还未能够显示页面,因为我们还不知道 DOM 元素的几何位置信息。那么接下来就需要计算出 DOM 树中可见元素的几何位置,我们把这个计算过程叫做布局。
我们看到,其实DOM树还含有很多不可见的元素,比如 head、script 标签,以及使用了 display:none 属性的元素。所以在显示之前,浏览器还要额外地构建一棵只包含可见元素布局树。还是前面的html代码为例,最终生成的布局树如下图所示:
现在我们有了一棵完整的布局树。那么接下来,浏览器就要计算布局树节点的坐标位置了。
首先是分层:
因为页面中可能存在很多复杂的效果,如一些复杂的 3D 变换、页面滚动,或者使用 z-index 做 z 轴排序等,为了更加高效的实现这些效果,渲染引擎还需要为特定的节点生成专用的图层,并生成一棵对应的图层树(LayerTree),你可以理解为就好像PS的图层一样。
接着是图层绘制: 渲染引擎把一个图层的绘制拆分成很多小的绘制指令,然后再把这些指令按照顺序组成一个待绘制列表,然后让其执行一个简单的绘制操作,比如绘制粉色矩形或者黑色的线等。而绘制一个元素通常需要好几条绘制指令,因为每个元素的背景、前景、边框都需要单独的指令去绘制。所以在图层绘制阶段,输出的内容就是这些待绘制列表。
之后是栅格化操作: 绘制列表只是用来记录绘制顺序和绘制指令的列表,而实际上绘制的操作是由渲染引擎中的合成线程来完成的。 渲染进程获取绘制列表后,就会生成位图,而这里的位图其实是由栅格化来生成的。栅格化是渲染进程的一个线程,所有的图块栅格化都是在这个线程池内执行的。
一旦所有图块都被光栅化,合成线程就会生成一个绘制图块的命令,将该命令提交给浏览器进程。浏览器进程里面有一个叫 viz 的组件,用来接收合成线程发过来的命令,然后将其页面内容绘制到内存中,最后再将内存显示在屏幕上。到这里,经过这一系列的阶段,编写好的 HTML、CSS、JavaScript 等文件,经过浏览器就会显示出漂亮的页面了。