在本篇笔记当中,我们将讲解导航流程,即从输入 URL 到页面展示这个过程发生了什么?
事实上在整个过程当中,实际上是需要各个进程之间的相互配合的。
在之前的笔记当中,我们介绍了关于浏览器架构的相关知识,了解了浏览器进程、渲染流程以及网络进程的主要职责。
- 浏览器进程主要负责用户交互,子进程管理和文件存储等功能。
- 网络进程则是面向渲染进程和浏览器进程等提供网络下载功能。
- 渲染进程的主要职责主要是把从网络中下载的资源解析为可以显示和交互的页面。
我们将导航过程大致描述如下;
- 首先用户需要从浏览器进程里输入请求信息。
- 随后网络进程发起请求。
- 接着服务器响应 URL 请求之后,浏览器进程开始准备渲染进程。
- 渲染进程准备好后,需要首先向渲染进程提交页面数据,我们称之为提交文档。
- 渲染进程接收完文档信息之后,便开始解析页面和下载子资源完成页面的最终渲染。
其中,用户发出 URL 请求,到页面开始解析的这个过程就叫做导航。
用户输入
当用户在浏览器的地址栏中输入一个查询关键字时,地址栏会首先判断我们输入的关键字究竟是搜索内容,还是请求的URL。
- 如果是搜索内容,地址栏将会使用浏览器默认的搜索引擎,来合成新的带搜索关键词的 URL。
- 如果判断输入内容符合 URL 规则,那么地址栏将会根据规则把这段内容加上 HTTP 协议整合为完整的 URL。
当浏览器开始加载一个地址之后,标签页上的图标便会进入加载状态,此时途中页面显示的依然是之前打开的页面,内容并没有立即替换为我们请求的页面,这是因为我们需要等待直到提交文档阶段页面内容才会被替换。
URL 请求过程
接下来便到了页面资源请求的这个过程。
首先浏览器进程会通过进程间通信技术 IPC 把 URL 请求发送至网络进程,网络进程收到 URL 请求后,会从这里开始发起真正的 URL 请求流程,而在这里的具体流程则是如下所述:
首先网络进程会查找本地缓存是否缓存了该资源,如果有缓存资源,那么直接返回资源给浏览器进程;如果在缓存中没有查找到该资源,那么就进入网络请求流程请求。
网络请求流程的第一步就是通过 DNS 解析以获取目的地址的 IP 地址,如果请求协议是https,那么还需要建立 TLS 连接,接下来的一系列请求过程,便与之前关于 HTTP 请求的笔记所描述的过程一致。
之前关于HTTP请求的笔记中,仅仅描述到了服务器在收到请求信息后作出响应,但并没有继续讲解当浏览器收到了服务器的响应信息后,浏览器接下来会做什么。
事实上等浏览器的网络进程接收到了服务器发送来的响应信息后,就会开始解析响应的内容。
重定向
在网络进程接受到了服务器返回的响应信息后,网络进程便会开始解析,如果发现返回的状态码是 301 或者 302,那么说明服务器需要浏览器重定向至其他 URL,这时网络进程就会根据响应信息中的 Location 字段读取重定向的地址,然后重新发起新的 HTTP 或者 HTTPS 请求。
而只有当最终的响应信息状态码是 200 的时候才说明信息一切正常便可以接下来继续处理后续内容。
响应数据类型
接下来我们需要考虑的问题是相应数据类型,由于我们的 URL 请求的数据类型并不统一,有的时候是资源下载类型,有的时候是正常的 HTML 页面,那么我们肯定首先需要考虑浏览器如何区分这些不同的类型。
浏览器区分数据类型的方案是通过字段 Content-Type。Content-Type 是 HTTP 头中一个非常重要的字段,它告诉浏览器与服务器返回的响应数据类型。随后浏览器便会根据 Content-Type 的值来决定如何显示响应体内容。
如果 Content-Type 字段的值被浏览器判断为下载类型,那么该请求就会被提交给浏览器的下载管理器,同时关于 URL 请求的导航流程就此结束;但是如果该字段的值被解析为 HTML 类型,那么浏览器则会继续进行导航流程。由于浏览器的页面渲染是运行在渲染进程中的,所以接下来就要开始准备渲染进程。
要注意的是,在这个地方进行的描述是准备渲染进程,而不是执行渲染进程。
准备渲染进程
默认情况下,Chrome 会为每个页面分配一个渲染进程,但是也会存在一些例外的情况,有的时候浏览器会让多个页面直接运行在同一个渲染进程中。我们首先需要区分在什么情况下多个页面会同时运行在一个渲染进程当中。
我们想要解决这个问题,首先需要了解一下什么是同一站点 same site。具体而言,我们可以将同一站点定义为根域名加上协议的组成结果下的所有子域名和不同的端口,比如下面三个都属于同一站点:
https://www.yucohny.github.io
https://time.yucohny.github.io
https://time.yucohny.github.io:3000
他们都属于同一站点,这是因为他们的协议都是 HTTPS,并且根域名都是 yucohnt.github.io,唯一的区分则是子域名和端口号不同。
Chrome 的默认策略是每个标签确实对应了一个渲染进程,但是如果从一个页面打开了另外一个新的页面,而新页面与原本页面都属于同一站点的话,那么新页面就会复用父页面的渲染进程,官方将这个默认策略叫做 process per site instance。
提交文档
此时我们的渲染进程就已经准备好了,但是我们并不能立即进入文档解析的状态,因为此时的文档数据仍然在网络进程当中,并没有提交给渲染进程,所以就到了提交文档阶段。
虽然我们是叫做提交文档这个名称,但是我们要明确,此处的「文档」是一个泛称,指的是响应体数据。
提交文档的消息由浏览器进程发出渲染进程,接收到提交文档的消息后,便会和网络进程建立传输数据的通道,当文档数据传输完成之后,渲染进程便会返回确认提交的消息给浏览器进程,浏览器进程在收到确认提交的消息后会更新浏览器的界面状态,包括安全状态地址栏的 URL,并更新 Web 整个页面。
这就解释了,在第一部分「用户输入」当中提到的,浏览器开始加载地址之后,并不会立即替换为我们请求的页面,这是因为在这个过程当中存在提交文档这么一个阶段。
渲染阶段
而一旦文档被成功提交到了渲染进程,那么渲染进程便会开始进行页面解析和子资源下载。而渲染阶段的整个流程,我们会在下篇笔记当中进行讲解。
但是至此,我们确实也已经解决了,「从在地址栏输入 URL 到页面展示,这个过程中发生了什么」这个问题。