前言

想要成为一名合格的前端工程师,掌握相关浏览器的工作原理是必备的,这样子才会有一个完整知识体系,要是**「能参透浏览器的工作原理,你就能解决 80% 的前端难题」**。

「其他文章:」

重点是**「女朋友面试的时候,这部分知识把她给挂了」**😹 所以准备写点…. 嗯~ o( ̄▽ ̄)o

还是那句话,「了解浏览器是如何工作的,能让你站在更高维度去理解前端」

希望通过这篇文章,能够让你重新认识浏览器,并把 JavaScript,网络,页面渲染,浏览器安全等知识串联起来,从而让你对整个前端体系有全新的认识。

**「从宏观的角度下看浏览器工作,大佬勿喷,你们的点赞与收藏是对我最大的支持」**😿

「读完这一期内容,你将收获」

  • 前端性能优化的底层逻辑;
  • 浏览器页面渲染的核心流程
  • JavaScript 运行机制解析
  • 浏览器网络及安全机制解析

快点开始吧,怎么这么墨迹呢?🔊

小声说:欢迎在留言区与我分享你的想法,也欢迎你在留言区记录你的思考过程👊

浏览器的架构了解多少

女朋友:❓❓❓ 我要你给我补浏览器原理相关知识,你跟我聊架构❓

我 (弱弱的说):你看呀,设计高性能 Web 应用,还是优化现有的 Web 应用,你都需要了解浏览器的网络流程,页面渲染过程,JavaScript 执行流程,以及 Web 安全理论,而这些功能是分散在浏览器的各个组件中的,通过浏览器的多进程架构学习,你可以把分散的知识点串起来,组成一张网,因此,学习浏览器的多进程架构是很有必要的

女朋友:那现在浏览器种类太多了,那多多少少都存在差异,那这个估计的讲半个小时?

我:不,我想 Chrome 最具有代表性,这是因为 Chrome、微软的 Edge 以及国内的大部分主流浏览器,都是基于 Chromium 二次开发而来;而 Chrome 是 Google 的官方发行版,特性和 Chromium 基本一样,只存在一些产品层面差异;嗯~ o( ̄▽ ̄)o🔊 那么以 Chrome 浏览器为例吧

我:进程与线程了解吗?

女朋友:啥?我听说过,但不是很清楚😰

进程与线程

要介绍进程与线程的话,需要先讲解下并行处理,了解了并行处理的概念,再理解进程和线程之间的关系就会变得轻松许多。

什么是并行处理

计算机中的并行处理就是同一时刻处理多个任务,比如我们要计算下面这三个表达式的值,并显示出结果。

A = 1+2
B = 20/5
C = 7*8
 
 

在编写代码的时候,我们可以把这个过程拆分为四个任务:

  • 任务 1 是计算 A=1+2;
  • 任务 2 是计算 B=20/5;
  • 任务 3 是计算 C=7*8;
  • 任务 4 是显示最后计算的结果。

正常情况下程序可以使用单线程来处理,也就是分四步按照顺序分别执行这四个任务

如果采用多线程,会怎么样呢?我们只需分 ” 两步走 “:第一步,使用三个线程同时执行前三个任务;第二步,再执行第四个显示任务。

通过对比分析,你会发现用单线程执行需要四步,而使用多线程只需要两步。因此,使用并行处理能大大提升性能

线程 VS 进程

多线程可以并行处理任务,但是线程是不能单独存在的,它是由进程来启动和管理的。那什么又是进程呢?

一个进程就是一个程序的运行实例。详细解释就是,启动一个程序的时候,操作系统会为该程序创建一块内存,用来存放代码、运行中的数据和一个执行任务的主线程,我们把这样的一个运行环境叫进程

看图👇

从图中可以看到,线程是依附于进程的,而进程中使用多线程并行处理能提升运算效率

进程和线程之间的关系有以下 4 个特点:

  1. 进程中的任意一线程执行出错,都会导致整个进程的崩溃。
  2. 线程之间共享进程中的数据。

  1. 从上图可以看出,线程 1、线程 2、线程 3 分别把执行的结果写入 A、B、C 中,然后线程 2 继续从 A、B、C 中读取数据,用来显示执行结果。
  2. 当一个进程关闭之后,操作系统会回收进程所占用的内存
  3. 进程之间的内容相互隔离

补充完基础,该开始讲正题了👇

单进程浏览器时代

顾名思义,单进程浏览器是指浏览器所以模块都运行再同一个进程里,这些模块包含了网络、插件、JavaScript 运行环境、渲染引擎和页面等。

单进程浏览器的架构如下图所示👇

如此多的功能模块运行在一个进程里,肯定有着不足的:

  • 不稳定性
  • 不流畅
  • 不安全

思考题:要是面试官要你详细说的话,你该怎么去表达清楚?

多进程浏览器时代

基于以上的问题,现代浏览器已经解决了这些问题了,是如何解决的呢?那我们聊一聊多进程时代

早期多进程架构

从图中可以看出,Chrome 的页面是运行在单独的渲染进程中的,同时页面里的插件也是运行在单独的插件进程之中,而进程之间是通过 IPC 机制进行通信(如图中虚线部分)

** 我们先看看如何解决不稳定的问题:** 由于进程是相互隔离的,所以当一个页面或者插件崩溃时,影响到的仅仅是当前的页面进程或者插件进程,并不会影响到浏览器和其他页面,这就完美地解决了页面或者插件的崩溃会导致整个浏览器崩溃,也就是不稳定的问题。

** 接下来再来看看不流畅的问题是如何解决的:** 同样,JavaScript 也是运行在渲染进程中的,所以即使 JavaScript 阻塞了渲染进程,影响到的也只是当前的渲染页面,而并不会影响浏览器和其他页面,因为其他页面的脚本是运行在它们自己的渲染进程中的。所以当我们再在 Chrome 中运行上面那个死循环的脚本时,没有响应的仅仅是当前的页面。

对于内存泄漏的解决方法那就更简单了,因为当关闭一个页面时,整个渲染进程也会被关闭,之后该进程所占用的内存都会被系统回收,这样就轻松解决了浏览器页面的内存泄漏问题。

最后我们再来看看上面的两个安全问题是怎么解决的: 用多进程架构的额外好处是可以使用安全沙箱,你可以把沙箱看成是操作系统给进程上了一把锁,沙箱里面的程序可以运行,但是不能在你的硬盘上写入任何数据,也不能在敏感位置读取任何数据,例如你的文档和桌面。Chrome 把插件进程和渲染进程锁在沙箱里面,这样即使在渲染进程或者插件进程里面执行了恶意程序,恶意程序也无法突破沙箱去获取系统权限。

以上都是我搬运过来的🖕 解释的比我清楚,也怕女朋友不懂😹

下面的才是我们的重点:目前的 Chrome 架构就是采用下面的方案,对于后面常见的面试题:从浏览器输入 URL 按回车到页面显示都发生了什么 这个经典面试题而言,有一个系统的知识体系,比背诵条例而言,更为重要!

目前多进程架构

Chrome 发展肯定是有新的变化的,我们先看看最新的 Chrome 进程架构,可以参考这个图👇

从图中可以看出,最新的 Chrome 浏览器包括:1 个浏览器(Browser)主进程、1 个 GPU 进程、1 个网络(NetWork)进程、多个渲染进程和多个插件进程。

下面我们来逐个分析下这几个进程的功能👇

  • 浏览器进程。主要负责界面显示、用户交互、子进程管理,同时提供存储等功能。
  • 渲染进程。核心任务是将 HTML、CSS 和 JavaScript 转换为用户可以与之交互的网页,排版引擎 Blink 和 JavaScript 引擎 V8 都是运行在该进程中,默认情况下,Chrome 会为每个 Tab 标签创建一个渲染进程。出于安全考虑,渲染进程都是运行在沙箱模式下。
  • GPU 进程。其实,Chrome 刚开始发布的时候是没有 GPU 进程的。而 GPU 的使用初衷是为了实现 3D CSS 的效果,只是随后网页、Chrome 的 UI 界面都选择采用 GPU 来绘制,这使得 GPU 成为浏览器普遍的需求。最后,Chrome 在其多进程架构上也引入了 GPU 进程。
  • 网络进程。主要负责页面的网络资源加载,之前是作为一个模块运行在浏览器进程里面的,直至最近才独立出来,成为一个单独的进程。
  • 插件进程。主要是负责插件的运行,因插件易崩溃,所以需要通过插件进程来隔离,以保证插件进程崩溃不会对浏览器和页面造成影响。

不过凡事都有两面性,虽然多进程模型提升了浏览器的稳定性、流畅性和安全性,但同样不可避免地带来了一些问题:

  • 更高的资源占用因为每个进程都会包含公共基础结构的副本(如 JavaScript 运行环境),这就意味着浏览器会消耗更多的内存资源。
  • 更复杂的体系架构浏览器各模块之间耦合性高、扩展性差等问题,会导致现在的架构已经很难适应新的需求了

未来面向服务的架构

为了解决这些问题,在 2016 年,Chrome 官方团队使用 “面向服务的架构”(Services Oriented Architecture,简称 SOA)的思想设计了新的 Chrome 架构

Chrome 最终要把 UI、数据库、文件、设备、网络等模块重构为基础服务,类似操作系统底层服务,下面是 Chrome” 面向服务的架构 ” 的进程模型图:

涉及面试题

为什么单进程浏览器当时不可以采用安全沙箱?

个人理解

如果一个进程使用了安全沙箱之后,该进程对于操作系统的权限就会受到限制,比如不能对一些位置的文件进行读写操作,而这些权限浏览器主进程所需要的,所以安全沙箱是不能应用到浏览器主进程之上的。

打开 Chrome 浏览器一个 Tab 页面,至少会出现几个进程?

个人理解

最新的 Chrome 浏览器包括至少四个: 1 个浏览器(Browser)主进程、1 个 GPU 进程、1 个网络(NetWork)进程、��个渲染进程和多个插件进程, 当然还有复杂的情况;

  1. 页面中有 iframe 的话, iframe 会单独在进程中
  2. 有插件的话,插件也会开启进程
  3. 多个页面属于同一站点,并且从 a 打开 b 页面,会共用一个渲染进程
  4. 装了扩展的话,扩展也会占用进程

这些进程都可以通过 Chrome 任务管理器来查看

即使如今多进程架构,还是会碰到单页面卡死的最终崩溃导致所有页面崩溃的情况,讲一讲你的理解?

个人理解

提供一种情况,就是同一站点, 围绕这个展开也行。

Chrome 的默认策略是,每个标签对应一个渲染进程。但是如果从一个页面打开了新页面,而新页面和当前页面属于同一站点时,那么新页面会复用父页面的渲染进程。官方把这个默认策略叫 process-per-site-instance。

更加简单的来说,就是如果多个页面符合同一站点,这几个页面会分配到一个渲染进程中去, 所以有这样子的一种情况, 一个页面崩溃了,会导致同一个站点的其他页面也奔溃,这是因为它们使用的是同一个渲染进程。

有人会问为什么会跑到一个进程里面呢?

你想一想呀, 属于同一家的站点,比如下面三个:

它们在一个渲染进程中的话,它们就会共享 JS 执行环境,也就是 A 页面可以直接在 B 页面中执行脚本了, 有些时候就是有这样子的需求嘛。

总结

女朋友:说的好多呀,就是记不住呐,能不能画个思维导图,或者总结一下👧

我:(这不是已经很简洁明了吗)看见女朋友这么期待的眼神😎 感觉安排

  • 早期浏览器:不稳定(单独进程) 不流畅(单独进程) 不安全(沙箱)
  • 早期多进程浏览器: 主进程 渲染进程 插件进程
  • 现代多进程架构: 主进程 渲染进程 插件进程 GPU 进程 网络进程
  • 未来面向服务架构

HTTP 请求流程

女朋友:怎么跟我说起 HTTP 请求流程呐,面试经常问输入 URL,中间发生的过程,我想要听这个,你别跟我说有的没的💢

我(小声说):小可爱你要是完成熟悉一个 HTTP 完整的工作流程,相信这些知识点对于你以后的学习或工作会很有帮助,而且这样子的话,你说的那个题目自然就会明白了,要不听我先聊一聊?

女朋友:(¬︿̫̿¬☆) 哼,看你能说出什么让我感兴趣的💢

整体流程

为了便于你理解,先看一张 HTTP 请求示意图,用来展现浏览器中的 HTTP 请求所经历的各个阶段。

从图中可以看到,浏览器中的 HTTP 请求从发起到结束一共经历了如下八个阶段:

  • 构建请求
  • 查找缓存
  • 准备 IP 和端口
  • 等待 TCP 队列
  • 建立 TCP 连接
  • 发起 HTTP 请求
  • 服务器处理请求
  • 服务器返回请求和断开连接

为了更好的把以上八个阶段讲清楚,我们举个具体的例子来说吧💪

浏览器端发起 HTTP 请求流程

如果你在浏览器地址栏里键入百度网站的地址:www.baidu.com, 那么接下来,浏览器会完成哪些动作呢?下面我们就一步一步详细 ” 追踪 ” 下。

1. 构建请求

首先,浏览器构建请求行信息(如下所示),构建好后,浏览器准备发起网络请求。

GET /index.html HTTP1.1
 
 

2. 查找缓存

在真正发起网络请求之前,浏览器会先在浏览器缓存中查询是否有要请求的文件。其中,浏览器缓存是一种在本地保存资源副本,以供下次请求时直接使用的技术

当浏览器发现请求的资源已经在浏览器缓存中存有副本,它会拦截请求,返回该资源的副本,并直接结束请求,而不会再去源服务器重新下载。这样做的好处有:

  • 缓解服务器端压力,提升性能(获取资源的耗时更短了);
  • 对于网站来说,缓存是实现快速资源加载的重要组成部分。

当然,如果缓存查找失败,就会进入网络请求过程了。

3. 准备 IP 地址和端口

不过,先不急,在了解网络请求之前,我们需要先看看 HTTP 和 TCP 的关系。因为浏览器使用 HTTP 协议作为应用层协议,用来封装请求的文本信息;并使用 TCP/IP 作传输层协议将它发到网络上,所以在 HTTP 工作开始之前,浏览器需要通过 TCP 与服务器建立连接。也就是说 HTTP 的内容是通过 TCP 的传输数据阶段来实现的,你可以结合下图更好地理解这二者的关系。

那接下来你可以思考这么 ” 一连串 ” 问题:

  • HTTP 网络请求的第一步是做什么呢?结合上图看,是和服务器建立 TCP 连接。
  • 那建立连接的信息都有了吗?建立 TCP 连接的第一步就是需要准备 IP 地址和端口号。
  • 那怎么获取 IP 地址和端口号呢?这得看看我们现在有什么,我们有一个 URL 地址,那么是否可以利用 URL 地址来获取 IP 和端口信息呢?

你会发现记住域名比 IP 更加的方便,所以基于这个需求又出现了一个服务,负责把域名和 IP 地址做一一映射关系。这套域名映射为 IP 的系统就叫做 “域名系统”,简称 DNS(Domain Name System)。

所以,这样一路推导下来,你会发现在第一步浏览器会请求 DNS 返回域名对应的 IP。当然浏览器还提供了 DNS 数据缓存服务,如果某个域名已经解析过了,那么浏览器会缓存解析的结果,以供下次查询时直接使用,这样也会减少一次网络请求。

拿到 IP 之后,接下来就需要获取端口号了。通常情况下,如果 URL 没有特别指明端口号,那么 HTTP 协议默认是 80 端口。

DNS 解析想深入了解的话,有兴趣的可以看看这篇文章 DNS 原理入门

4. 等待 TCP 队列

现在已经把端口和 IP 地址都准备好了,那么下一步是不是可以建立 TCP 连接了呢?

答案不一定的,这个得根据不同的浏览器来规定的,我们以 Chrome 浏览器为例,Chrome 有个机制,同一个域名同时最多只能建立 6 个 TCP 连接,如果在同一个域名下同时有 10 个请求发生,那么其中 4 个请求会进入排队等待状态,直至进行中的请求完成。

当然,如果当前请求数量少于 6,会直接进入下一步,建立 TCP 连接。

这里其实也就可以出个小面试题,那有很多图片资源或者其他支援请求怎么办?

5. 建立 TCP 连接

排队等待结束之后,终于可以快乐地和服务器握手了,在 HTTP 工作开始之前,浏览器通过 TCP 与服务器建立连接。而 TCP 的工作方式,要张开细讲的话,有很多细节需要把握,可以看看这篇文章:

UDP 协议详解

  • UDP 在传输的过程中数据包容易丢失
  • 大文件被拆分为很多小的数据包时,小数据包会经不同的路由,不能同时到达接受端,UDP 不知道如何组装这些数据包,UDP 也不会有重发机制

TCP 的出现,就是为了解决以上的问题,它是面向连接的,可靠的,基于字节流的传输层通行协议

一个完整的 TCP 连接生命周期包括了 “建立连接""传输数据” 和 “断开连接” 三个阶段。

  • 这里面要细讲的话,内容就多了,比如三次握手建立连接,四次挥手断开连接,很多文章也详细的介绍了整个的过程,这里就不介绍了,可以看看我推荐的文章,关于讲这些内容的。

6. 发送 HTTP 请求

上面大概讲完了建立 TCP 连接,你可以理解 TCP 的连接,是为了保证浏览器跟服务器更好的通信。当然了有了上面的这个过程中, HTTP 中的数据也正是在这个通信过程中传输的。

那么我们从一张图片中来看,浏览器是如何发送信息给服务器的👇

  • 浏览器向服务器发起请求行:请求方法,请求 URL HTTP 协议版本
  • 请求行大概意思就是告诉服务器,我需要做上面,比如 GET 方法,我需要向你拿资源,POST 方法通常也就是我需要向服务器提高什么数据,需要注意的就是如果是 POST 方法,浏览器还需要准备好数据,通过请求体发送给服务器。
  • 请求头:把浏览器的基础信息告诉服务器,比如包含了浏览器所使用的操作系统,浏览器的内核信息等,还有请求的域名信息,浏览器的 Cookie 信息等等。

🤖怎么样,是不是听完这些,懂了一些皮毛了,那接下来看看服务器端是如何处理 HTTP 的请求吧✍

7. 服务器端处理 HTTP 请求流程

此时你可以理解成 HTTP 请求信息终于送到到服务器,接下来服务器会根据请求信息来准备相应的内容啦

7.1 返回请求

服务器处理好后,便把结果数据返回给浏览器,我们来看看放回的数据是怎么样的👇

  • 响应行:HTTP 协议版本 状态码,通过常见的状态码,就可以知道处理的结果
  • 常见状态码:200 表示处理成功。 如果没有找到页面,状态码为 404

状态码类型很多,网上很多资料,可以自行查阅,还是推荐两篇比较好的文章:

具有代表性的 HTTP 状态码

面试必考之 http 状态码有哪些

  • 响应头:你可以理解成包含服务器自身的一些信息,比如服务器生成数据的时间,返回数据的类型(HTML,流媒体,JSON,XHTML 等等)以及服务器在客户端保存的 Cookie 等信息
  • 响应头中 Cache-Control 字段也很重要,这个涉及到了 HTTP 缓存,这个字段涵义就是设置缓存资源的时间的
  • 发送完响应头后,服务器就可以继续发送响应体的数据,通常,响应体就包含了 HTML 的实际内容。

8.1 断开连接

一般情况下,服务器发送完数据后,就要关闭 TCP 连接。不过有一种情况比较特殊,我们来看看

Connection:Keep-Alive 
 
 

如果浏览器或者在服务器中加入其头信息如上面的字段的话,TCP 连接会仍然保持,这样子浏览器就可以通过同一个 TCP 连接发送请求,保存 TCP 连接可以省下去下次请求需要建立连接的时间,提升资源加载速度。

比如,一个 Web 页面中内嵌的图片就都来自同一个 Web 站点,如果初始化了一个持久连接,你就可以复用该连接,以请求其他资源,而不需要重新再建立新的 TCP 连接。

重定向

我们还得聊一聊一种特殊的情况,不过这个情况跟之前提过的状态码有关,我们大概知道了,服务器返回的状态码不同,会有不同的返回的结果,你肯定遇到过这样子的情况吧:当你在浏览器中打开 baidu.com 后,你会发现最终打开的页面地址是 www.baidu.com ** 这两个 URL 不一样的原因就是涉及到了重定向 **,让我们从一张图片上面看看这种情况吧👇

我们看看响应行返回的状态码 301,状态 301 告诉浏览器,你需要重新转到另外一个网址,需要重定向的地址正式包含在响应头的 Location 字段中。接下啦,浏览器获取 Location 字段中的地址,重新导航,这也就是完整的重定向的执行流程。

这解释了为什么输入 baidu.com 后,最终打开的是 www.baidu.com

涉及面试题

  • 为什么很多站点第二次打开速度会很快?🚀
  • 当登录过一个网站之后,下次再访问该站点,就已经处于登录状态了,这是怎么做到的呢?
  • 如何使用 Cookie 来进行状态管理,说一说流程
  • TCP 建立连接过程讲一讲,为什么握手需要三次?
  • UDP 了解吗,与 TCP 相比,优点是啥,缺点呢?
  • 你刚刚说了 TCP 连接会存在 TCP 队列,那加载大量图片或者其他资源的时候,该怎么解决卡顿呢

当然了我只能说考察点太多了,光是 http 协议就可以问很多的问题,我只是提出一个例子,只有你一步步的去分析并提出一些问题,让疑问带领你去学习,抓住问题的本质学透相关知识点,让你站在一个更高的维度去查看整体框架。

就拿第一个相关点聊一聊吧,为什么会加快打开速度

既然第二次能加快网页的打开速度,肯定第一次加载页面的时候,缓存了耗时的数据

从上面介绍的核心流程来看,比如 DNS 和页面资源缓存这两块的数据会被浏览器缓存下来。

DNS 缓存相对就简单些了,这可以很好看看上面的文章,这里就不展开篇幅介绍了。

浏览器缓存

这个不就是面试常考的吗,聊一聊浏览器的缓存,这里面自然就涉及到常见 header 中字段

  • Cache-Control(重要策略)
  • Expires
  • Last-modified
  • ETag

缓存流程如图:

浏览器缓存的展开的话,又是很大的内容,看看这篇 浏览器缓存

通过上面了解到,浏览器把资源缓存到本地,浏览器缓存直接使用本地副本来回应请求,不会产生真实的网络请求,从而节省了时间,加快了访问的速度🚀

总结

  • HTTP 请求从发起到结束一共经历了如下八个阶段:
  • 构建请求、查找缓存、准备 IP 和端口、等待 TCP 队列、建立 TCP 连接、发起 HTTP 请求、服务器处理请求、服务器返回请求和断开连接
  • 了解 HTTP 完整的工作流程,这个整体的框架,有着很多分析问题的思路就在里面,<(^-^)>然后在深入了解每个阶段具体怎么运作的,对你日后学习或者工作会有所帮助。

从宏观视角去看 HTTP 请求流程,有了一个整体框架,接下来就是一步步的分析提出问题,带着疑惑去学习,抓住问题的本质,比如深入的了解 TCP 如何连接的,这样子何尝不是一种学习方法呢👊

导航流程:从输入 URL 到页面展示,这中间发生了什么

女朋友 (出现):面试必考题,给我先讲一讲整体流程吧,我拿笔记下来✍

我:嗯~ o( ̄▽ ̄)o,我想着只能给你讲整体流程,太细的内容,自然需要你自己一步步去分析,带着问题去了解👊

整体流程

先看一张流程图

从图中可以看出,存在进程间的通信(IPC), 先回顾一下上面章节讲的进程的职责吧。

  • 浏览器进程主要负责用户交互、子进程管理和文件储存等功能
  • 网络进程是面向渲染进程和浏览器进程等提供网络下载功能
  • 渲染进程是把 HTML,CSS,JavaScript,图片等资源解析为可以显示和交互的页面。

通常渲染进程是在安全沙箱中的,Chrome 这样子的安全措施还是有必要的,你可以理解成渲染进程中所以的内容都是通过网络获取的,可能这过程中存在一些恶意代码,这类代码利用浏览器漏洞对系统进行攻击,所以就很有必要…..

通过上面的图片,这个过程大致如下:

  1. 浏览器进程发出 URL 请求给网络进程
  2. 网络进程接收到 URL 请求后,发起网络请求,然后服务器返回 HTTP 数据到网络进程,网络进程解析 HTTP 响应头数据,并将其转发给浏览器进程
  3. 浏览器进程接收到网络进程的响应头数据后,发送 CommitNavigation 消息到渲染进程,发送 CommitNavigation 时会携带响应头、等基本信息。
  4. 渲染进程接收到 CommitNavigation 消息之后,便开始准备接收 HTML 数据,接收数据的方式是直接和网络进程建立数据管道
  5. 最后渲染进程会像浏览器进程 ” 确认提交 “,这是告诉浏览器进程,说我已经准备好接受和解析页面数据了
  6. 最后浏览器进程更新页面状态

上述就是经历的主要阶段,下面就详细的分析这些阶段吧👇

尝试分析整体流程

  1. 用户输入 URL

浏览器会根据用户输入的信息判断是搜索还是网址,如果是搜索内容,就将搜索内容 + 默认搜索引擎合成新的 URL;如果用户输入的内容符合 URL 规则,浏览器就会根据 URL 协议,在这段内容上加上协议合成合法的 URL

比如输入 www.baidu.com 地址栏会根据规则,把这段内容加上协议,合成完整的 URL,如 https://www.baidu.com

用户输入完内容,按下回车键,浏览器导航栏显示 loading 状态,但是页面还是呈现前一个页面,这是因为新页面的响应数据还没有获得。

当然了这里面的话,有个 beforeunload 事件,该事件允许页面退出之前执行一些数据的清理操作,还可以询问用户是否要离开当前页面,比如当前页面有未提交的表单,用户可以通过 beforeunload 事件来取消导航,让浏览器不在进行后续工作。

  1. URL 请求过程

浏览器进程将构建请求行数据,进行进程间通信(IPC)将 URL 请求发送给网络进程,类似于下面这个:

GET /index.html HTTP1.1
 
 
  1. 网络进程获取到 URL,先去本地缓存中查找是否有缓存文件,如果有,拦截请求,直接 200 返回;否则,进入网络请求过程

  2. 网络进程请求 DNS 返回域名对应的 IP 和端口号,如果之前 DNS 数据缓存服务缓存过当前域名信息,就会直接返回缓存信息;否则,发起请求获取根据域名解析出来的 IP 和端口号,如果没有端口号,http 默认 80,https 默认 443。如果是 https 请求,还需要建立 TLS 连接。

  3. 在进程 TCP 连接的过程中,Chrome 有个机制,同一个域名下最多只能建立 6 个 TCP 连接,如果在同一个域名下有 10 个请求发生,那么其中 4 个请求会进入等待转台,直至进行中的请求完成。如果请求个数小于 6,会直接建立 TCP 连接。

  4. TCP 三次握手建立连接,http 请求加上 TCP 头部——包括源端口号、目的程序端口号和用于校验数据完整性的序号,向下传输。

  5. 网络层在数据包上加上 IP 头部——包括源 IP 地址和目的 IP 地址,继续向下传输到底层

  6. 底层通过物理网络传输给目的服务器主机,紧接着目的服务器主机网络层接收到数据包,解析出 IP 头部,识别出数据部分,将解开的数据包向上传输到传输层。

  7. 目的服务器主机传输层获取到数据包,解析出 TCP 头部,识别端口,将解开的数据包向上传输到应用层

  8. 应用层 HTTP 解析请求头和请求体,如果需要重定向,HTTP 直接返回 HTTP 响应数据的状态 code301 或者 302,同时在请求头的 Location 字段中附上重定向地址,浏览器会根据 code 和 Location 进行重定向操作;如果不是重定向,首先服务器会根据 请求头中的 If-None-Match 的值来判断请求的资源是否被更新,如果没有更新,就返回 304 状态码,相当于告诉浏览器之前的缓存还可以使用,就不返回新数据了;否则,返回新数据,200 的状态码,并且如果想要浏览器缓存数据的话,就在相应头中加入字段:

    Cache-Control:Max-age=2000
     
     

    响应数据又顺着应用层——传输层——网络层——网络层——传输层——应用层的顺序返回到网络进程

  9. 数据传输完成,TCP 四次挥手断开连接。如果,浏览器或者服务器在 HTTP 头部加上如下信息,TCP 就一直保持连接。保持 TCP 连接可以省下下次需要建立连接的时间,提示资源加载速度

    Connection:Keep-Alive
     
     
  10. 网络进程将获取到的数据包进行解析,根据响应头中的 Content-type 来判断响应数据的类型,如果是字节流类型,就将该请求交给下载管理器;如果是 text/html 类型,就通知浏览器进程获取到文档准备渲染

    从返回的响应头信息来看,其 Content-Type 的值是 application/octet-stream,显示数据是字节流类型的,通常情况下,浏览器会按照下载类型来处理该请求。

    需要注意的是,如果服务器配置 Content-Type 不正确,比如将 text/html 类型配置成 application/octet-stream 类型,那么浏览器可能会曲解文件内容,比如会将一个本来是用来展示的页面,变成了一个下载文件。

  11. 浏览器进程获取到通知,根据当前页面 B 是否是从页面 A 打开的并且和页面 A 是否是同一个站点(根域名和协议一样就被认为是同一个站点),如果满足上述条件,就复用之前网页的进程,否则,新创建一个单独的渲染进程。

  12. 浏览器会发出 ” 提交文档 ” 的消息给渲染进程,渲染进程收到消息后,会和网络进程建立传输数据的 ” 管道 “,文档数据传输完成后,渲染进程会返回 ” 确认提交 ” 的消息给浏览器进程。

  13. 浏览器收到 ” 确认提交 ” 的消息后,会更新浏览器的页面状态,包括了安全状态、地址栏的 URL、前进后退的历史状态,并更新 web 页面,此时的 web 页面是空白页。

这也就解释了为什么在浏览器的地址栏里面输入了一个地址后,之前的页面没有立马消失,而是要加载一会儿才会更新页面。
 
 
  1. 渲染进程对文档进行页面解析和子资源加载,HTML 通过 HTM 解析器转成 DOM Tree(二叉树类似结构的东西),CSS 按照 CSS 规则和 CSS 解释器转成 CSSOM TREE,两个 tree 结合,形成 render tree(不包含 HTML 的具体元素和元素要画的具体位置),通过 Layout 可以计算出每个元素具体的宽高颜色位置,结合起来,开始绘制,最后显示在屏幕中新页面显示出来。

导航流程很重要,它是网络加载流程和渲染流程之间的一座桥梁,如果你理解了导航流程,那么你就能完整串起来整个页面显示流程,这对于你理解浏览器的工作原理起到了点睛的作用。

当然了渲染阶段是每个前端工程师需要熟悉并且掌握的,下一篇文章将会仔细把渲染流程过一遍的✍

下一期:

渲染阶段很重要,了解了相关流程可以让你 ” 看透 ” 页面是如何工作的,有了这些知识点的话,相信你可以解决一部分的问题,比如熟悉使用开发者工具,能优化页面卡顿问题,使用 JavaScript 优化动画流程,通过优化样式表来防止强制同步布局,等等。

女朋友:哇,我好像确实对浏览器流程有了整体的认识,确实一定程度上帮助我理解了浏览器运行原理👏,什么时候跟我讲一讲渲染流程的细节呀,我还想继续去学习呐🏃

我开(nan)心(guo)的说:就这两天整理一下吧,到时候给你写出来✍

参考

从浏览器多进程到 JS 单线程,JS 运行机制最全面的一次梳理

DNS 原理解析

写给前端工程师的 DNS 基础知识

TCP/UDP 协议详解

具有代表性的 HTTP 状态码

面试必考之 http 状态码有哪些

浏览器缓存

推荐👍👍👍极客时间 - 浏览器工作原理与实践