HTML转换为页面,其实就是HTML、CSS、JavaScript通过中间渲染模块的处理,
最终输出为页面。
HTML、CSS和JavaScript的含义:
![HTML、CSS和JavaScript](https://img2018.cnblogs.com/blog/1141454/201912/1141454-20191225154714684-1176554809.jpg)
**HTML,超文本标记语言,由标记和文本组成**。标记也称为**标签**,每个标签都有它
的语意,浏览器会根据标签的语意来正确展示HTML内容。
**CSS,层叠样式表,由选择器和属性组成**,通过CSS可以改变HTML的字体颜色,大小等信息。
**JavaScript,一种应用于web端的轻量级,解释型或即时编译型的编程语言**。一般会作为
页面脚本,实现页面动态效果。
了解了HTML、CSS、JavaScript后,我们开始分析渲染模块。
渲染模块在执行过程中会被划分为很多子阶段,输入的HTML经过子阶段最后输出
像素,这样的一个处理流程叫**渲染流水线**。
渲染流水线示意图:
![渲染流水线](https://img2018.cnblogs.com/blog/1141454/201912/1141454-20191225154714942-142555304.jpg)
按照渲染的时间顺序,流水线可分为:**构建DOM树、样式计算、布局阶段、分层、绘制、
分块、光栅化和合成**。
## 构建DOM树
**由于浏览器无法直接解析HTML,所以需要将HTML转换为浏览器能够理解的结构,DOM树**。
树结构类似于生活中的“树”,其中每个点称为**节点**,相连的节点称为**父子节点**。
DOM树构建过程示意图:
![DOM树构建过程](https://img2018.cnblogs.com/blog/1141454/201912/1141454-20191225154715165-1260283092.jpg)
构建DOM树的输入内容是一个HTML文件,然后经过HTML解析器解析,最终输出树状结构的DOM。
通过“开发者工具”->“Console”控制台,输出“document”回车后可查看完整的DOM树结构。
可以了解到,DOM和HTML内容几乎是一样的,但是和HTML不同的是,DOM是保持在内存中树状结构,
可以通过JavaScript来查询和修改内容。
而要让DOM节点拥有正确的样式,需要样式计算。
## 样式计算
样式计算的目的是为了计算出DOM节点中每个元素的具体样式,该阶段可分成三步:
### 1、把CSS转换为浏览器能够解析的结构
HTML加载CSS的三种方式:
![HTML加载CSS的三种方式](https://img2018.cnblogs.com/blog/1141454/201912/1141454-20191225154715519-1721273838.jpg)
CSS样式主要由三种加载方式:
* 通过link引用的外部CSS文件
*
所以元素有了层叠上下文的属性或者需要被剪裁,那么就会被提升成为单独一层,你可以参看下图:
从上图我们可以看到,document层上有A和B层,而B层之上又有两个图层。这些图层组织在一起也是一颗树状结构。
图层树是基于布局树来创建的,为了找出哪些元素需要在哪些层中,渲染引擎会遍历布局树来创建层树(Update LayerTree)。
```
例如所示,文字所显示的区域超过了200*200的显示范围时就产生了剪裁,渲染引擎会把剪裁文字内容
的一部分用于显示在div区域。
出现这种剪裁情况下,渲染引擎会为文字部分单独创建一个层,如果出现滚动条,滚动条也会
被提升为单独的层。
被裁剪的内容所在单独图层的示意图:
![被裁剪的内容所在单独图层的示意图](/chrome/draw13.png)
所以,**元素有了层叠上下文的属性或者需要被检查,满足任意一点,就会被提升为单独的一层**。
## 图层绘制
在完成图层树的构建后,渲染引擎会对图层树中的每个图层进行绘制,渲染引擎会把每个图层的
绘制拆分成很多小的绘制指令,然后再把这些指令按照顺序组成一个待绘制列表。
绘制列表:
![绘制列表](/chrome/draw14.png)
绘制列表中的指令其实就是让其执行一个简单的绘制操作,而每个原生的背景、边框等都需要单独
的指令绘制,通过几条绘制指令来实现绘制一个元素。
“开发者工具”->“Layers”可查看“document”层等的绘制列表过程:
![一个图层的绘制列表](/chrome/draw15.png)
## 栅格化操作
绘制列表只是用来记录绘制顺序和绘制指令的列表,实际上绘制操作是由渲染引擎
中的合成线程来完成的。
渲染进程中的合成线程和主线程:
![渲染进程中的合成线程和主线程](/chrome/draw16.png)
当图层的绘制列表准备完成后,主线程会把该绘制列表提交给合成线程。
通常一个页面很大,而用户有时只能看到其中一部分,所以屏幕上页面的可见区域叫视口(ViewPort)。
这种情况下,要绘制出所有图层内容的话产生的开销太多,所以,**合成线程会将图层划分为图块(title)**,
这些图片的大小通常是256*256或512*512。
图层图块示意图:
![图层划分为图块示意图](/chrome/draw17.png)
**合成线程会按照视口附件的图块来优先生成位图,实际生成位图的操作是由栅格化来执行的。
所谓栅格化,是指将图块转换为位图**。而图块是栅格化执行的最小单位。渲染进程
维护了一个栅格化的线程池,所以的图块栅格化都是在线程池内执行的。
合成线程提交图块给栅格化线程池:
![合成线程提交图块给栅格化线程池](/chrome/draw18.png)
通常,栅格化过程都会使用GPU来加速生成,使用GPU生成位图的过程叫快速栅格化或者
GPU栅格化,渲染进程会把生成图块的指令发送给GPU进程,然后在
GPU进程中执行生成图块的位图,生成的位图被保存在GPU内存中。
GPU栅格化:
![GPU栅格化](/chrome/draw19.png)
## 合成和显示
所有图块都被栅格化,合成线程就会生成一个绘制图块的命令,“DrawQuad”,然后
将该命令提交给浏览器进程。
浏览器进程里面有一个叫viz的组件,用来接收合成线程发过来的DrawQuad命令,然后根据
DrawQuad命令将其页面内容绘制到内存中,最后将内存显示在屏幕上。
## 总结
完整的渲染流水线:
![完整的渲染流水线](/chrome/draw20.png)
综上,一个完整的渲染流程为:
1、渲染进程将HTML内容转换为浏览器能识别的**DOM树**结构。
2、渲染引擎将CSS样式表转化为浏览器可以理解的**styleSheets**,计算出
DOM节点的样式。
3、创建**布局树**,并计算元素的布局信息。
4、对布局树进行分层,并生成**分层树**。
5、为每个图层生成**绘制列表**,并将其提交给合成线程。
6、合成线程将图层分成**图块**,并在**栅格化线程池**中将图块转换成位图。
7、合成线程发送绘制图块命令**DrawQuad**给浏览器进程。
8、浏览器进程根据DrawQuad消息生成页面,并显示到显示器上。
## 推荐阅读
HTML标签:[w3school](https://www.w3school.com.cn/html/html_primary.asp)
CSS:[w3school](https://www.w3school.com.cn/css/index.asp)
JavaScript:[MDN](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript)
层叠上下文属性:[https://developer.mozilla.org/zh-CN/docs/Web/Guide/CSS/Understanding_z_index/The_stacking_context](https://developer.mozilla.org/zh-CN/docs/Web/Guide/CSS/Understanding_z_index/The_stacking_context)
> 文章优先发布于公众号[前端美食汇],关注公众号获取最新内容吧~~~