本文首发于托儿所的前端秘籍
[跟面试官说我是这样理解浏览器渲染流程的(上)]
跟面试官说我是这样理解浏览器渲染流程的(中)上文中已经介绍了如何DOM生成、样式计算、布局和分层,剩下的内容将在本文介绍完成,让我们开始吧。
图层绘制
在完成图层树的构建后渲染引擎就会对图层树中的每个图层开始绘制。试想一下,如果让你画一个国旗你会怎么操作:
- 先画一个长方形
- 填充红色的背景颜色
- 画五角星
- 填充黄色
渲染引擎在渲染图层的绘制阶段跟这个相似,会把每个图层的绘制拆分成很多小的指令,然后再把这些指令按顺序组成一个待绘制的列表,如图所示:
绘制操作
从上图可以看出绘制列表中的指令非常简单,就是让其执行一个简单的操作,比如绘制矩形等。而绘制一个元素通常需要好几个执行,因为每个元素会有背景,边框,颜色等。所以在绘制图层阶段输出的内容就是这些待绘制的列表。
如果想更直观的了解绘制图层可以打开"开发者工具"的 "Layers" 标签,选择 "document" 层查看,如下图所示:
图中区域1是 "document" 的绘制列表,拖动区域2中的进度条可以重现绘制列表的过程。
栅格化
绘制列表只是用来记录绘制顺序和绘制指令的列表,实际绘制操作是由渲染引擎中的合成引擎完成的,如下图所示:
合成线程和主线程
如上图所示主线程子啊准备好图层的绘制列表之后会将该绘制列表提交给合成线程,那合成线程是怎么工作的呢?
首先我们了解一个概念什么是视口(viewport):
视口代表当前可见的计算机图形区域。在 Web 浏览器术语中,通常与浏览器窗口相同,但不包括浏览器的 UI,菜单栏等------即指你正在浏览的文档的那一部分。
文档,比如这篇文章,可能会非常长。你的视口就是你现在所能见到的所有事物。值得注意的是"什么是视口区域"这个问题,页面中的一些导航菜单也包括在其中。视口的大小取决于屏幕的大小,无论浏览器是否处于全屏模式,是否被用户缩放了。视口外的区域,比如这个文档的参见部分,可能需要滚动到其所在的区域才会出现在屏幕上。
- 在尺寸较大的设备中,在这些设备上,应用显示区域不一定是全屏的,视口是浏览器窗口的大小。
- 在大多数移动设备中,浏览器是全屏的,视口是整个屏幕的大小。
- 在全屏模式下,视口是设备屏幕的范围,窗口是浏览器窗口,浏览器窗口大小小于或等于视口的大小,并且文档是这个网站,文档的大小可比视口长或宽。
概括地说,视口基本上是当前文档的可见部分。
viewport
在有些情况下,有的图层很大,比如有的页面你的滚动条很长,要滚动很久才能到底部,但是通过视口用户只能看到页面的很小一部分,所以在这种情况下,要绘制图层的所有内容就会产生很大的开销,基于这个原因,合成线程会将图层划分为图块,这些图款通常为256*256
或 512*512
。
然后合成线程会安装视口附近的图块来优先生成位图,实际生成位图的操作是由栅格化来执行的。所谓栅格化是指将图块转换为位图。而图块是栅格化执行的最小单位。渲染进程维护了一个栅格化的线程池,所有的图块栅格化都是在线程池内执行的。
通常栅格化的过程都是由 GPU 来加速完成的,使用 GPU 生成位图的过程叫快速栅格化,或者 GPU 栅格化,生成的位图保存在 GPU 中。
大家应该都知道 GPU 操作是在 GPU 进程汇总,如果执行栅格化操作用到了 GPU,那么这就涉及到了跨进程操作,具体如下图:
GPU栅格化
从上图可以看出渲染进程把生成图块的指令发送到 GPU,然后再GPU中生成图块的位图,并保存到 GPU 中。
合成和显示
一旦所有图块都被光栅化,合成线程就会生成一个绘制图块的命令------ "DrawQuad",然后将该命令提交给浏览器进程。
浏览器进程里面有一个叫viz的组件,用来接收合成线程发过来的 "DrawQuad" 命令,然后根据" DrawQuad" 命令,将其页面内容绘制到内存中,最后再将内存显示在屏幕上。
到这里,经过这一系列的阶段,编写好的 HTML、CSS、JavaScript 等文件,经过浏览器就会显示出漂亮的页面了。
渲染流水线大总结
好了,我们现在已经分析完了整个渲染流程,从HTML到DOM、样式计算、布局、图层、绘制、光栅化、合成和显示。下面我用一张图来总结下这整个渲染流程:
渲染流水线大总结
结合上图,一个完整的渲染流程大致可总结为如下:
- 渲染进程将HTML内容转换为能够读懂的DOM树结构。
- 渲染引擎将CSS样式表转化为浏览器可以理解的styleSheets,计算出DOM节点的样式。
- 创建布局树,并计算元素的布局信息。
- 对布局树进行分层,并生成分层树。
- 为每个图层生成绘制列表,并将其提交到合成线程。
- 合成线程将图层分成图块,并在光栅化线程池中将图块转换成位图。
- 合成线程发送绘制图块命令DrawQuad给浏览器进程。
- 浏览器进程根据DrawQuad消息生成页面,并显示到显示器上。