浏览器原理之从输入一个网址开始

1,217 阅读8分钟

前言

浏览器于前端,就像盘子于厨师,再好的烹饪技术,都需要端出去品尝下,才知是否真材实料。

所以呢,用好浏览器,掌握浏览器原理,是每一个前端开发必修的一门课。最近呢,正好在学习极客时间的《浏览器原理》,毕竟这么好的资料,看完当然得沉淀一下,于是乎,就有了本文。

一、概览

浏览器架构

打开一个页面任务管理器情况

主流程

二、用户输入

根据规则,把这段内容加上协议,合成为完整的 URL。

  1. 判断输入的关键字是搜索内容,还是请求的 URL

  2. 如果是搜索内容:使用浏览器默认的搜索引擎,来合成新的带搜索关键字的 URL

  3. 如果是地址:添加协议头等信息

三、网络请求

强缓存

浏览器发送请求前,根据请求头的expirescache-control判断是否命中(包括是否过期)强缓存策略,如果命中,直接从缓存获取资源,并不会发送请求。如果没有命中,则进入下一步。

DNS解析

为了获取请求服务真实的IP

TCP三次握手

第一次握手

客户端向服务端发送连接请求报文段。该报文段中包含自身的数据通讯初始序号。请求发送后,客户端便进入 SYN-SENT 状态,x 表示客户端的数据通信初始序号。

第二次握手

服务端收到连接请求报文段后,如果同意连接,则会发送一个应答,该应答中也会包含自身的数据通讯初始序号,发送完成后便进入 SYN-RECEIVED 状态。

第三次握手

当客户端收到连接同意的应答后,还要向服务端发送一个确认报文。客户端发完这个报文段后便进入ESTABLISHED 状态,服务端收到这个应答后也进入 ESTABLISHED 状态,此时连接建立成功。

HTTPS握手

  1. 客户端发送第一个随机值client-random,需要的协议加密方式

  2. 服务端收到客户端的随机值,自己也产生第二个随机值service-random,并根据客户端需要的协议和加密方式来使用对应的方式,发送自己的证书(如果需要验证客户端证书需要说明)

  3. 客户端收到服务端的证书并验证是否有效,验证通过会通过证书的公钥加密client-random + service-random生成第三个随机值pre-master并发送给服务端。

  4. 服务端收到加密过的随机值pre-master,会使用私钥解密pre-master获得3个随机值后,按照之前规定的加密方式,生成密钥master secret 并发送确认消息给浏览器。

  5. 这时候客户端也将拥有的三个随机值client-randomservice-randompre-maste,按照之前约定的加密方式生成密钥master secret,接下来的通信就可以通过该密钥来加密解密

参考

HTTPS

构建请求

响应请求

四、渲染页面

1. 准备渲染进程

那什么叫同一站点呢?其实就是我们平时知道的同源策略

同源策略

例子

不复用进程

打开QQ页面,因为新增了标签页新增了一个进程

从该页面打开一个新闻页,因为新增了标签页,也不同站,所以新增了一个进程

image.png

复用进程

从一个网站点击打开一个新标签页,该标签页同站,所以复用了进程。但浏览器直接新增标签页,然后打开同站的网址,不会复用进程

从新闻页面国际栏目进入军事栏目,没有新增标签页,也是同站,所以复用了进程

2. 提交文档

因为需要做处理这些流程,所以在浏览器的地址栏里面输入了一个地址后,之前的页面没有立马消失,而是要加载一会儿才会更新页面。

3. 渲染阶段

整体流程

浏览器的核心之中的核心就是在渲染阶段,所以,后面的篇幅,会详细介绍这一块。

五、渲染阶段

下面这张图,相信很多人已经很熟悉了。

而我们主要讲的就是下面这几部分,会一一展开介绍。

1. 构建DOM树

主要流程是

Token : 分为 Tag Token文本 Token。对应我们的起始标签、终止标签、和文本内容

例子:

2. 样式计算(生成渲染树)

转换CSS样式为styleSheets

CSS的来源为下图这三个,优先级为元素style > <style>标签内CSS > <link>引用的CSS文件

转换完成以后,可以看到,document里面会有一个styleSheets ,此时的styleSheets 才是浏览器能理解的数据结构,就像DOM要转换为TOKEN一样。

属性值标准化

也就是像我们平时会有一斤、一两的重量说法,全部转换成千克制

样式计算

说样式计算,就会有两个概念。

  • **继承规则:**每一个节点都可能包含父节点的样式

  • **层叠规则:**定义了如何合并来自多个源的属性值的算法

关于继承,可以看到,浏览器有很多默认的样式,这些样式每一个dom节点都继承了,但是是否使用在节点上,这就得看

  1. 假设继承了父属性a,是否有对应同一个dom的同一个属性,记b

  2. 如果b的优先级大于a 那样式就采用b的。

那么问题来了,怎么判断哪一个样式的优先级高呢?这里可以直接参考MDN的优先级说明

而关于层叠样式表,其实目的也是确定样式优先级的,参考MDN层叠规则

基于这两个规则,最后每一个DOM都会生成一个Computed 的样式

3. 布局阶段(生成布局树)

前面生成的渲染树,就像是商家推销的家具,各种吹,各种好,什么适合我们。怎么选,选好以后送到家里,怎么摆放,都是需要思考的。而布局阶段就是这么一个作用,他会计算每一个DOM节点的位置信息,然后保存在布局树中。

主要逻辑

主要流程

4. 图层构建(生成图层树)

就如PS图层概念一样,浏览器会把要展示的元素,就行分层,其实这样做的目的就是跟ps一样,**方便局部渲染。**例如,这是一个爱奇艺的页面图层,可以看到,分了14个层。

一般来说,并不是布局树的每个节点都包含一个图层,如果一个节点没有对应的层,那么这个节点就从属于父节点的图层。而会创建一个新图层的条件有这些

最后,通过这么一些规则,就根据布局树,生成图层树

5. 图层绘制阶段(生成绘制列表)

就跟画油画一样,都有个一个步骤,先描线、后上远景色,后上近景色。简单来说图层绘制阶段就是生成绘制的指令表的一个阶段。

主要流程

下图就是一个图层的指令列表

6. 分块

为什么要分块呢?就跟我们搬家一样,往往一些床啊,柜子啊啥的,都很大,拿不进门里,这时候就需要我们先拆一下,一块一块拿进去了。所以呢,页面同理,页面大小完全可能超过视窗大小。

例如下面的这个示意图。

7. 栅格化(生成位图)

栅格化简单来说就是将我们的图块转换成位图。那什么是位图呢?其实就是浏览器渲染进程能理解的01集合。而合成线程会按照视口附近的图块来优先生成位图

主要流程是

  • 每一个图块调用栅格化线程

  • 栅格化线程将图块绘制指令给到GPU生成位图,并保存在GPU内存中

8. 合成

这一阶段就像我们做需求上线一样,是浏览器交接给用户的最终阶段。

9. 总结

  1. 渲染进程将 HTML 内容转换为能够读懂的DOM 树结构。

  2. 渲染引擎将 CSS 样式表转化为浏览器可以理解的styleSheets,计算出 DOM 节点的样式,形成ComputedStyle

  3. 创建布局树,将元素的布局信息的信息保存在树中。

  4. 对布局树进行分层,并生成图层树

  5. 为每个图层生成绘制列表,并将其提交到合成线程

  6. 合成线程将图层分成图块,并在光栅化线程池中将图块转换成位图

  7. 合成线程发送绘制图块命令DrawQuad给浏览器进程。

  8. 浏览器进程根据 DrawQuad 消息生成页面,并显示到显示器上。

六、相关概念

参考

极客时间《浏览器原理》

juejin.cn/post/693523…

juejin.cn/post/684490…