前言
浏览器于前端,就像盘子于厨师,再好的烹饪技术,都需要端出去品尝下,才知是否真材实料。
所以呢,用好浏览器,掌握浏览器原理,是每一个前端开发必修的一门课。最近呢,正好在学习极客时间的《浏览器原理》,毕竟这么好的资料,看完当然得沉淀一下,于是乎,就有了本文。
一、概览
浏览器架构
打开一个页面任务管理器情况
主流程
二、用户输入
根据规则,把这段内容加上协议,合成为完整的 URL。
-
判断输入的关键字是搜索内容,还是请求的 URL
-
如果是搜索内容:使用浏览器默认的搜索引擎,来合成新的带搜索关键字的 URL。
-
如果是地址:添加协议头等信息
三、网络请求
强缓存
浏览器发送请求前,根据请求头的expires和cache-control判断是否命中(包括是否过期)强缓存策略,如果命中,直接从缓存获取资源,并不会发送请求。如果没有命中,则进入下一步。
DNS解析
为了获取请求服务真实的IP
TCP三次握手
第一次握手
客户端向服务端发送连接请求报文段。该报文段中包含自身的数据通讯初始序号。请求发送后,客户端便进入 SYN-SENT 状态,x 表示客户端的数据通信初始序号。
第二次握手
服务端收到连接请求报文段后,如果同意连接,则会发送一个应答,该应答中也会包含自身的数据通讯初始序号,发送完成后便进入 SYN-RECEIVED 状态。
第三次握手
当客户端收到连接同意的应答后,还要向服务端发送一个确认报文。客户端发完这个报文段后便进入ESTABLISHED 状态,服务端收到这个应答后也进入 ESTABLISHED 状态,此时连接建立成功。
HTTPS握手
-
客户端发送第一个随机值
client-random,需要的协议和加密方式 -
服务端收到客户端的随机值,自己也产生第二个随机值
service-random,并根据客户端需要的协议和加密方式来使用对应的方式,发送自己的证书(如果需要验证客户端证书需要说明) -
客户端收到服务端的证书并验证是否有效,验证通过会通过证书的公钥加密
client-random+service-random再生成第三个随机值pre-master并发送给服务端。 -
服务端收到加密过的随机值
pre-master,会使用私钥解密pre-master获得3个随机值后,按照之前规定的加密方式,生成密钥master secret并发送确认消息给浏览器。 -
这时候客户端也将拥有的三个随机值
client-random、service-random、pre-maste,按照之前约定的加密方式生成密钥master secret,接下来的通信就可以通过该密钥来加密解密
参考
构建请求
响应请求
四、渲染页面
1. 准备渲染进程
那什么叫同一站点呢?其实就是我们平时知道的同源策略
同源策略
例子
不复用进程
打开QQ页面,因为新增了标签页,新增了一个进程
从该页面打开一个新闻页,因为新增了标签页,也不同站,所以新增了一个进程
复用进程
从一个网站点击打开一个新标签页,该标签页同站,所以复用了进程。但浏览器直接新增标签页,然后打开同站的网址,不会复用进程
从新闻页面国际栏目进入军事栏目,没有新增标签页,也是同站,所以复用了进程
2. 提交文档
因为需要做处理这些流程,所以在浏览器的地址栏里面输入了一个地址后,之前的页面没有立马消失,而是要加载一会儿才会更新页面。
3. 渲染阶段
整体流程
浏览器的核心之中的核心就是在渲染阶段,所以,后面的篇幅,会详细介绍这一块。
五、渲染阶段
下面这张图,相信很多人已经很熟悉了。
而我们主要讲的就是下面这几部分,会一一展开介绍。
1. 构建DOM树
主要流程是
Token : 分为 Tag Token 和文本 Token。对应我们的起始标签、终止标签、和文本内容。
例子:
2. 样式计算(生成渲染树)
转换CSS样式为styleSheets
CSS的来源为下图这三个,优先级为元素style > <style>标签内CSS > <link>引用的CSS文件
转换完成以后,可以看到,document里面会有一个styleSheets ,此时的styleSheets 才是浏览器能理解的数据结构,就像DOM要转换为TOKEN一样。
属性值标准化
也就是像我们平时会有一斤、一两的重量说法,全部转换成千克制。
样式计算
说样式计算,就会有两个概念。
-
**继承规则:**每一个节点都可能包含父节点的样式
-
**层叠规则:**定义了如何合并来自多个源的属性值的算法
关于继承,可以看到,浏览器有很多默认的样式,这些样式每一个dom节点都继承了,但是是否使用在节点上,这就得看
-
假设继承了父属性
a,是否有对应同一个dom的同一个属性,记b -
如果
b的优先级大于a那样式就采用b的。
那么问题来了,怎么判断哪一个样式的优先级高呢?这里可以直接参考MDN的优先级说明
而关于层叠样式表,其实目的也是确定样式优先级的,参考MDN层叠规则
基于这两个规则,最后每一个DOM都会生成一个Computed 的样式
3. 布局阶段(生成布局树)
前面生成的渲染树,就像是商家推销的家具,各种吹,各种好,什么适合我们。怎么选,选好以后送到家里,怎么摆放,都是需要思考的。而布局阶段就是这么一个作用,他会计算每一个DOM节点的位置信息,然后保存在布局树中。
主要逻辑
主要流程
4. 图层构建(生成图层树)
就如PS图层概念一样,浏览器会把要展示的元素,就行分层,其实这样做的目的就是跟ps一样,**方便局部渲染。**例如,这是一个爱奇艺的页面图层,可以看到,分了14个层。
一般来说,并不是布局树的每个节点都包含一个图层,如果一个节点没有对应的层,那么这个节点就从属于父节点的图层。而会创建一个新图层的条件有这些
最后,通过这么一些规则,就根据布局树,生成图层树了
5. 图层绘制阶段(生成绘制列表)
就跟画油画一样,都有个一个步骤,先描线、后上远景色,后上近景色。简单来说图层绘制阶段就是生成绘制的指令表的一个阶段。
主要流程
下图就是一个图层的指令列表
6. 分块
为什么要分块呢?就跟我们搬家一样,往往一些床啊,柜子啊啥的,都很大,拿不进门里,这时候就需要我们先拆一下,一块一块拿进去了。所以呢,页面同理,页面大小完全可能超过视窗大小。
例如下面的这个示意图。
7. 栅格化(生成位图)
栅格化简单来说就是将我们的图块转换成位图。那什么是位图呢?其实就是浏览器渲染进程能理解的01集合。而合成线程会按照视口附近的图块来优先生成位图。
主要流程是
-
每一个图块调用栅格化线程
-
栅格化线程将图块绘制指令给到GPU生成位图,并保存在GPU内存中
8. 合成
这一阶段就像我们做需求上线一样,是浏览器交接给用户的最终阶段。
9. 总结
-
渲染进程将 HTML 内容转换为能够读懂的DOM 树结构。
-
渲染引擎将 CSS 样式表转化为浏览器可以理解的styleSheets,计算出 DOM 节点的样式,形成ComputedStyle。
-
创建布局树,将元素的布局信息的信息保存在树中。
-
对布局树进行分层,并生成图层树。
-
为每个图层生成绘制列表,并将其提交到合成线程。
-
合成线程将图层分成图块,并在光栅化线程池中将图块转换成位图。
-
合成线程发送绘制图块命令DrawQuad给浏览器进程。
-
浏览器进程根据 DrawQuad 消息生成页面,并显示到显示器上。
六、相关概念
参考
极客时间《浏览器原理》