主要是介绍浏览器从显示网页整体做了那些具体流程,主要分析渲染部分
![](https://img-blog.csdnimg.cn/img_convert/ee106eceb85ef495d81c6baacb16399f.png)
1,解析HTML
解析我们的HTML,生成DOM树结构
首先会拿到html的整体的字符串,进行标记化(token)
为什么要标记化,因为浏览器是不能识别这些字符串需要进行标记化的处理,本质上就是把这段字符串的html进行标签类型的拆分,因为是一长串的字符串需要对字符串进行解析成node节点,所以会进行标记化,方便浏览器根据标记的标签去进行DOM树渲染
![](https://img-blog.csdnimg.cn/img_convert/ea3bb774976bb355c4cd5e04ff8041e8.png)
但是我们在解析HTML的过程当中我们也会碰到style和link这样的标签,这些样式和交互的内容就涉及到CSS的解析
浏览器在解析的时候为了提高效率会启动一个预解析线程,这个线程主要做的就是扫描出外部的style和js文件进行异步下载解析,解析完成后如果渲染主线程没有没有完成会直接把解析的结果CSSOM添加到主线程中
style和link标签的解析和下载都是在预线程当中进行的,所以css下载不了的情况下也不会影响主线程的进度。最后css** 标记化(token)也会形成一颗CSSOM**树
![](https://img-blog.csdnimg.cn/img_convert/df1f0c9473532e111fffcded509f4f4b.png)
CSSOM
![](https://img-blog.csdnimg.cn/img_convert/43c71df9aefd4aed0420970315d74024.png)
但是JS文件会阻塞HTML解析是为什么呢?
因为主线程解析到script标签的时候,会停止解析HTML,并且会等待js文件下载好,然后解析完毕之后才继续进行解析,因为js代码是可以修改当前的DOM树的形成的,所以代码必须要等JS文件解析完毕之后才能继续生成DOM树
![](https://img-blog.csdnimg.cn/img_convert/ef52fd500d3d5b3b146f1aefe114ad35.png)
通常script的标签都是在body标签的底部,这样就不会因为碰到较大的js文件下载而影响我们的首屏渲染
在最近几年的版本的浏览器中也提供了以下方式避免了js代码阻塞渲染的情况
async
defer
prefetch
preload
当HTML解析渲染完成后我们会得到DOM树和CSSOM树,这是浏览器渲染的第一步
![](https://img-blog.csdnimg.cn/img_convert/385f3b20ebee0f798cfe8edb5c64102d.png)
2,样式计算
接下来把解析出来的COMTree和CSSOMTree结合到一起
主线程会遍历DOM树的每个节点最终计算出每个节点的样式,称之位Computed Style
在这个计算的过程中,我们的预设值会变成绝对值,比如我们的color:red会变成 color:rgb(255,0,0),相对单位会变成绝对单位,比如em会变成px
![](https://img-blog.csdnimg.cn/img_convert/88bf9a00e5b4765cf54e26d399398db0.jpeg)
浏览器确定每个节点的样式之后,会形成样式规则树,这颗树上记录了每一个DOM节点的样式
另外计算属性会对每一个节点进行所有样式的属性值赋值,如果某个节点开发时没有赋予样式,将使用样式的默认值或者继承植
经过样式计算后会把DOM树和CSSOM树结合成带有样式的DOM树如下图
![](https://img-blog.csdnimg.cn/img_convert/c8a06262df9fc16a80180a60aaf792f9.png)
3,布局
当浏览器解析HTML和样式计算后已经知道每个DOM节点所附带的样式了,但是还不足以呈现在页面上,因为我们还缺少每个元素在页面上的位置布局!
主线程这个时候会递归遍历完刚刚构建好的DOM树,由于DOM树上挂载了计算样式,就可以计算出布局树(layout tree),布局树上的每一个节点都挂载了它在页面上的位置也就是X,Y的坐标以及盒子模型的大小等几何信息
![](https://img-blog.csdnimg.cn/img_convert/c43f66bdbac00b6a7c4730e20e4c6d83.png)
虽然生成了布局树,但是布局树的和之前的DOM树存在一些差异,
比如当有点节点挂载了display:none的样式时,就不会在布局树上展示
还有就是虽然伪类元素不会在DOM树上展示,但是如果伪类元素如果有几何信息它就会在布局树上展示
还有比较少有人知道的匿名行盒,匿名块盒等也会导致布局树和DOM树的结构差异化
![](https://img-blog.csdnimg.cn/img_convert/68691887eef8daa26aa382c2d4470976.png)
4,分层
当我们的布局树构建完成之后,接下来的一步就是分层!
为什么要分层呢?
分层的好处在于,改变某一层级的元素时,只会对该层级产生影响,从而提高效率
主线程会遍历整个布局树生成一个层次树(Layer Tree)确定元素需要放在那一层
![](https://img-blog.csdnimg.cn/img_convert/a8be550d0e98687e24356612eb801e5e.png)
滚动条,堆叠上下文,transform,opacity等样式对少都会影响分层的结果,也可以通过z-index,will-change属性对其进行分层、
will-change 为web开发者提供了一种告知浏览器该元素会有哪些变化的方法,这样浏览器可以在元素属性真正发生变化之前提前做好对应的优化准备工作。 这种优化可以将一部分复杂的计算工作提前准备好,使页面的反应更为快速灵敏。
5,生成绘制指令
分层结束之后就开始绘制指令的生成了
主线程会给每一层单独生成一个绘制的指令集,用于生成该层的图像生成
![](https://img-blog.csdnimg.cn/img_convert/c496aedfc50eeef3aa14a2e85a59e383.png)
当主线程进行到这一步的时候你要注意了虽然生成了绘制的指令,但是还没有执行只是生成了指令,而且这个时候渲染主线程基本上已经完结了,接下来的工作将交给合成线程去完成
![](https://img-blog.csdnimg.cn/img_convert/802f933bb44b8cb82d91e58adf4cd6ec.png)
6,分块
这个时候就是合成线程开始工作了
合成线程它首先会对每个图层进行一些分块,划分为更小的区域
![](https://img-blog.csdnimg.cn/img_convert/f987e7635f67a12d0409b00dc02a1f1f.png)
当完成分块之后,合成线程就不会和主线程一样单独进行了,而是会申请更多的线程同时去完成分块内的工作!
![](https://img-blog.csdnimg.cn/img_convert/db6aa15696044eff306b8dabbc022402.png)
7,光栅化
当我们的分块完成之后,接下来就光栅化的阶段了,更简单的说就是确认每一个像素点的rbg颜色信息
光栅化的操作不是由合成线程来做的,而是合成线程交给GPU进程,GPU进程会以极高的速度完成光栅化
GPU是专门干图形化处理的工作,他会开启多个线程进行绘制
![](https://img-blog.csdnimg.cn/img_convert/f0c10da4f6c2ec3351ce083690f3af70.png)
8,绘制
最终我们才进行绘制,这是最后一步
所有的图形块被光栅化之后,合成线程就拿到了每个图层以及每个块的位置信息,从而生成了一个个的【指引】信息
指引会标识出每个图块应该渲染到屏幕的哪个位置,并且会考虑到旋转,缩放等变形效果
程旋转,缩放等效果都发生在合成线程当中,和渲染主线程没有关系。所以为什么transform效率高就是这个原因
![](https://img-blog.csdnimg.cn/img_convert/8050a421c0b8a976594c3c06126e5579.png)
总结
现在我们回顾一下浏览器拿到HTML文本到最终渲染的一个整体流程
![](https://img-blog.csdnimg.cn/img_convert/0d28a02d2d68e1c29269d14e4bf47cd7.png)
问题:重排和重绘
重排(回流):
触发重排:当我们的渲染树发生元素的尺寸,结构或者属性发生变化的时候,浏览器会重新解析dom树和css树
![](https://img-blog.csdnimg.cn/img_convert/74a07a3e3f76e8d480d63b52c5f7455f.png)
重绘:
触发重绘:当页面中某些元素的样式发生变化,但不会影响其在文档流中的位置时,浏览器就会对元素进行重新绘制。
![](https://img-blog.csdnimg.cn/img_convert/7b3f3248800a76e2ee85fac006731691.png)
总结:这也是为什么重排的触发比重绘更加影响性能的渲染,因为重排会触发渲染主的流程的重新渲染,而重绘只需要重新执行合成线程