1. 用户输入URL
当用户在地址栏输入内容时,浏览器会判断输入的是关键字还是请求URL。如果是关键字,那么会使用浏览器默认搜索引擎结合关键字生成URL;如果是URL,浏览器则会请求URL。这其中都是在浏览器进程中实现的。
2. 请求URL过程
接下来就会进入页面加载URL的过程。浏览器进程会通过进程间通信(IPC)把 URL 交给网络进程,网络进程接收到才会开始发起真正的URL请求。
首先网络进程会查找本地缓存是否有缓存过该资源,如果有,则直接返回给浏览器进程;如果没有,则进入网络请求流程。请求前会进行DNS解析DNS解析过程,就是把请求域名转换成IP地址,如果协议是 HTTPS ,还需要建立TLS连接,用于浏览器和服务器传输数据之间提供保密性和数据完整性。
因为应用层协议 HTTP 是基于传输层 TCP 的,所以真正传输数据之前,先要进行TCP连接,也就是我们常说的TCP三次握手。
TCP连接建立后,浏览器端会构建网络请求行,请求头,请求体,域名相关的Cookie添加到请求头上,然后向服务器发送构建的请求信息。服务器接收到请求信息,然后会根据请求信息生成响应数据(响应行,响应头,响应体),通过TCP连接返回给浏览器的网络进程。
网络进程接收到响应头后,就开始解析。如果返回的状态码是 301(永久重定向) 或者 302(临时重定向),这就说明浏览器要重定向到新的URL。这时网络进程会读取响应头内的 Location 字段的 URL ,发起新的请求,网络进程的流程又要重新开始了。如果返回的状态码是200,那么网路进就会继续往下处理。
接下来处理的就是类型的处理,那是怎么判断返回的类型的呢?那就是Content-Type,这个字段就是告诉浏览器,服务器响应的数据类型是什么。那常见的Content-Type类型都有哪些呢?
Content-Type: text/html // html类型
Content-Type: application/javascript; // js类型
Content-Type: image/jpeg // jpeg图片类型
Content-Type: application/json; // json类型
3. 准备渲染进程
如果返回的类型是 html,那么这个时候浏览器会交给渲染进程来处理,默认情况下,浏览器会给每一个新页面分配一个新的渲染进程,但也有例外,如果一个页面打开另外一个新的页面是相同域名(根域名和协议相同)的,两个页面会共用同一个渲染进程。
这个过程只是浏览器根据返回的类型,确定是否需要创建一个新的渲染进程来处理,这个时候并不会立即对返回数据进行处理,因为这个时候数据还在网络进程,并没有交给渲染进程,所以下一步要进入提交文档阶段。
4. 提交文档
响应体的数据要从网络进程交给渲染进程,但这之间需要浏览器进程进行协调,协调的具体做法就是通过“提交文档”的方式,当渲染进程接收到“提交文档“的消息,那么就会和网络进程建立数据传输通道,当响应体数据都传输完成后,渲染进程会返回”确认提交“的消息给浏览器进程。浏览器进程收到消息后,会更新浏览器界面状态,包括地址栏的URL,前进后退按钮的历史状态,安全状态,并且会更新Web页面(把旧的页面进行替换,此时为白屏)。
5. 进入渲染阶段
渲染进程在接收到响应的数据便开始页面解析和子资源加载(页面上的css,js文件等等)。渲染按照时间顺序可以划分为几个阶段:构建DOM树,样式计算,布局阶段,分层,绘制,分块,光栅化,合成。
-
构建DOM树
因为浏览器无法理解和使用HTML,所以要把HTML转换成浏览器能理解的结构,DOM树。
-
样式计算
把css转换成浏览器理解的结构。当渲染引擎接收到css文本的手,因为浏览器不能直接理解纯文本的css样式,需要转换成styleSheets结构。
标准化样式表中的值。将所有值转换为渲染引擎容易理解的、表转化的值。
计算出DOM树中每个节点的具体样式。这里就涉及到css的继承规则和层叠规则了。以下是css继承的选择过程。
层叠是css的一个基本特征,它是用来合并来自多个源的属性值的算法。
这个阶段的目的就是计算出DOM节点中每个元素的具体样式,这个过程中遵守继承和层叠两个规则,这个过程后就得到一个带有样式属性的DOM树。
-
布局树生成
首先要构建出一个布局树,遍历DOM树把所有可见的节点加入布局,把不可见的节点(如:head标签下的内容)忽略。这样就构建出一个布局树。
-
分层(生成图层树)
虽然有一个布局树,但是有一些复杂的3D效果,
z-index做z轴排序的,为了更好实现这些效果,渲染引擎为特定的节点生成一个图层树(Layout Tree)。如果你用过PS,那就很容易理解这里分层的概念,正是这些图层叠加构成最终的页面效果。 -
绘制(生成绘制指令)
完成图层树的构建后,渲染进程会把一个图层的绘制分成很多小的绘制指令。在这个阶段,输出的内容就是等待绘制的指令列表。
-
栅格化
绘制指令生成后,绘制操作会交给渲染进程中的合成线程完成。
一般来说,合成线程会将图层分成多个图块,通常是256 x 256,512 x 512。为什么要图层划分成小图块呢?这是因为图层可能很大,但实际上通过浏览器视口要滚动很久才能看完,所以一开始没有必要把完整的图层全部渲染出来,增加开销。
合成线程会根据视口附近的图块来优先生成位图。这其实就是栅格化,就是将图块转换为位图。渲染进程会维护一个栅格化的线程池。整个栅格化过程都会用到GPU来加速,所以也叫GPU栅格化。
-
合成
一旦所有图块都被栅格化,合成线程会发送一个DrawQuad绘制图块命令给浏览器进程,将其页面内容绘制到内存中,最后显示在屏幕上。
渲染进程其实是一个沙盒状态,因为渲染页面渲染生成完成后,渲染进程会发消息给浏览器进程,浏览器接收到消息后,会停止标签上的加载动画。至此,页面就渲染完成了。
6. 断开连接
经过上面几步就完成了浏览器发送数据给服务器,服务器响应对应的数据给浏览器。如果这个时候没有再使用这个TCP 连接,那么就要断开这个TCP连接,就是常说的“四次挥手”
但如果浏览器或者服务器在其头部加入了Connection:Keep-Alive,那么这个TCP连接发送完仍然保持着连接状态,一般保持2小时。下一次如果有相同域名的请求,那么会直接使用这个TCP连接,这样做第二次相同域名的请求就不需要进行再重新建立TCP连接了。保持连接状态其实和HTTP协议版本有关系。
1. http 1.0 默认一次tcp连接进行一次http请求之后就会断开(由于资源浪费,有些服务器支持通过配置支持多次)
2. http 1.1 请求头配置:Connection:keep-alive = true,只要tcp连接不断开(默认2小时),一直可以进行http请求,但是一个tcp连接同一时间只支持一个http请求 Connection:keep-alive = false,只能发一次http请求
3. http 2.0:多路复用技术Multiplexing,一个tcp可以并发多个http请求(理论无上限,但是一般浏览器会有tcp并发数的限制)