前言
我们知道浏览器是多进程架构的,比如浏览器进程、渲染器进程、插件进程等等。而进程中又有不同的线程,比如浏览器进程中包含UI线程、存储线程、网络线程等等(chorme进行了架构更新,一些线程被抽离成单独的进程,在资源不足的设备上采用旧方案)。接下来我将结合提到的进程线程来介绍:当我们从浏览器地址栏中输入URL之后导航的全过程。
处理输入
当我们打开浏览器的那一瞬间开始,浏览器进程开始工作。
当我们开始在地址栏中输入时,浏览器进程中的UI进程开始进行判断:输入的数据是要搜索查询的数据还是一段URL?当是进行搜索查询时,则发送到默认设置的搜索引擎;若是URL则开始请求URL的网站。
开始导航(请求)
- 当用户按下回车键后,UI线程会启动网络调用以获取站点内容,同时选项卡加载转圈。
- 网络线程开始经历这些过程:构建请求、查找强缓存(若命中则直接使用)、DNS解析得到ip地址、建立TCP连接、发送HTTP请求。
在此过程中,网络线程可能会收到服务器重定向标头(例如HTTP 301),在这种情况下,网络线程与请求重定向的UI线程通信,随后,启动另一个URL请求
if Service Worker
当要访问的URL注册设置了Service Worker,且不是第一次请求时(有缓存,能够在浏览器缓存的列表中找到),UI线程则找到渲染进程运行Service Worker代码,可以从**缓存中加载数据(因此无需请求网络)**或从网络加载新的资源。
读取响应
当响应主体开始进入时,网络线程将在必要时查看流的前几个子节(响应头中的Content-Type标头)来确定是什么数据类型,但是由于可能丢失或错误, 因此在此处进行MIME嗅探。
当确定文件的类型之后,如果是HTML文件,则将数据传递到渲染进程,但如果是zip文件等等,则将数据传给下载管理器进行下载。与此同时,网络线程也将进行安全检查:恶意名单检查(如果域和响应数据在恶意站点名单中,则会发出和显示警告页面。)、跨域读取检查(CrossOriginReadBlock检查,敏感的跨站点数据不进入渲染进程)
查找渲染器进程
当读取响应完成,并进行完所有检查之后,网络线程将告知UI线程,UI线程将对渲染进程进行寻找。
但由于网络请求可能经过几百毫秒才能获取响应,因此为了加速此过程,当网络线程开始发起请求时,UI线程同时也在主动寻找或启动渲染器进程。因此当读取响应完成后,渲染器进程早已准备就绪。
提交导航
此时数据和渲染器进程都准备就绪,数据将通过IPC从浏览器进程发送到渲染器进程以提交导航。一旦浏览器进程监听到渲染器进程中进行提交的确认时,导航即完成,文档开始正式加载。
同时将会发生:
- 地址栏更新,安全指示符和站点设置UI,以反映新页面的站点信息。
- 更新标签的会话历史记录,因此后退/前进按钮将逐步浏览刚刚导航到的站点。
- 磁盘存储会话历史记录,以在关闭选项卡或窗口时促进选项卡/会话还原。
初步加载完成
在提交导航后,渲染器将继续加载资源以渲染页面(页面渲染的具体过程)。一旦渲染器进程“完成”(onload事件在所有帧上触发执行完成后)渲染,它就会通过IPC发送回浏览器进程,UI线程也将在选项卡上停止转圈。
因为JS可能会导致视图的重新渲染,因此此时并非完全的加载完成
导航到其他站点
当再次在地址栏中进行输入时,浏览器将会重复以上所有过程。但在离开当前页面之前,浏览器将将会检查当前访问的网站是否监听了beforeunload事件
总结
- 整个页面的渲染过程主要分为以下六个步骤:处理输入、开始导航、读取响应、查找渲染器进程、提交导航、初步加载完成。
- 在开始导航的整个过程中,需要注意页面是否注册了Web Worker且为第二次访问
- 查找渲染器进程实际上是与开始导航并发执行的,当读取响应完成时,渲染器进程其实已经准备完毕
- 初步加载完成并非完全加载完成,因为JS等资源可能对视图进行改变