详细解释从输入 url 到页面渲染发生什么,一篇文章总结所有浏览器原理 + 计算机网络的面试题,建议收藏

本篇文章从一个经典的面试题入手,几乎涵盖了所有关于浏览器工作原理和计算机网络的面试题目,内容比较长,东西非常多,难免疏漏,有不对的地方欢迎指正。

经典面试题【从输入 url 到页面渲染发生什么】,我将从3个主要模块把这块的知识点串成串,分别是浏览器工作原理、计算机网络请求、dom渲染相关。在之前我的这篇文章已经大概描述了整个过程,可以说这篇文章是之前的文章的扩展版本。

一、用户在浏览器的地址栏输入内容

随便打开一个浏览器你会发现可以在地址栏里输入内容或者是一个网址,所以这块浏览器都会在内部做区分的。

1.1 输入判断

浏览器根据用户的输入信息,进行判断输入的是搜索内容还是网址,这个很好判断,可以根据一定的规则判断是否是 http、https开头,是否是 www 开头等,每个浏览器有自己的规则。这部分工作是浏览器的【浏览器进程】完成的。

了解浏览器的多进程模型,浏览器的组成,浏览器的多进程模型,浏览器的内核-CSDN博客

由此可见,对于用户输入的判断,属于是和用户的交互,所以这一部分的判断是【浏览器进程】负责完成的。

浏览器进程也叫浏览器主进程,或者简称主进程。

1.1.1 输入内容

如果是输入的内容,那么浏览器的浏览器进程会使用浏览器设置的默认的搜索引擎,搜索输入的内容形成一个完整的 URL

浏览器可以自定义默认的搜索引擎,比如 chrome

测试一下,我当前的设置使用bing 搜索作为我的默认搜索引擎,在地址栏输入了 【你好】,会得到一个 URL ,这个拼接操作就是浏览器进程实现的

 得到了一个完整的 URL ,才进行下一步工作,接下来的工作和直接在地址栏输入一个 URL 一致。

1.1.2 输入网址

如果根据规则判断出,输入的内容是网址,如果该网址没有协议,那么就会根据规则加上协议,比如输入 www.baidu.com 那么他会自动加上 https 协议,拼接成完整的地址 https://www.baidu.com。【这一步还是不通浏览器的处理规则可能不通】

1.2 按下回车

1.2.1 beforeunload 触发

我们可以监听 window.beforeunload 事件,在当前浏览器的窗口关闭或者刷新的时候触发,当前页面不会直接关闭,可以点击确定按钮关闭或者刷新,也可以取消关闭或刷新

我相信以前都是在用,但是没有细想其中的原理,在页面刷新之前执行 beforeunload 事件。

我们来测试一下 chrome 浏览器。

(1)对于已经全部加载完的网页,浏览器左上角的图表示这样的,有一个html中设置的favicon.ico图标icon,和一个刷新的按钮

(2)触发 beforeunload 事件之后,弹出一个确认弹框,同时 favicon.ico 图标变成 loading 的状态,刷新按钮变成取消按钮,可以点击取消按钮取消页面的加载。

也就是说浏览器的一个标签页上有一个图标代表了当前页面加载状态,有一个按钮可以刷新页面或者取消加载。对于这个图标和按钮的管理由浏览器的【浏览器进程】负责。

1.2.2 更改图标状态

不管你有没有监听 beforeunload 事件,【浏览器进程】都把浏览器标签页面的图标状态先更改为【加载中】,此时浏览器的页面暂时保持不变,因为还没有新的页面数据返回。

如果你监听了 beforeunload 事件,那么在 beforeunload 事件执行之后才会开始导航,如果你没监听,那么就是在你按下回车之后就开始导航了。

1.2.3 开始导航

【浏览器进程】开始导航,接下来的任务就是【网络进程】的工作了,网络进程接收到浏览器进程拼接的URL,开始发起网络请求。

浏览器进程通过进程间通信 IPC 把 URL 请求发送到网络进程

IPC是啥?Inter-Process Communication 进程间通信,注意这是一个通用的说法,不只是浏览器之间的进程通信叫 IPC,操作系统中的进程间通信也叫 IPC

真正的 URL 请求是由【网络进程】发起的,要不然人家叫网络进程呢。

1.3 浏览器缓存缓存

网络进程现在有一个完整的 URL,在发起请求之前,会先检查本地缓存中是否有对应的缓存文件,这个就是浏览器的缓存机制。如果有对应的且有效的缓存文件,那么网络进程就不需要发起后续的请求了。

我们在开发过程中遇到点问题,总是说是浏览器缓存导致的,那么浏览器的缓存这不就来了,他就是在这一步发挥作用呢。浏览器缓存也是老生常谈的面试题了,其实也一点也不难,就是概念性的问题,背一下就知道,有什么难的呢,一切都是纸老虎

关于浏览器的缓存问题,我在这篇文章已经有详细的解释,欢迎大家关注查看。

二、DNS 解析

终于进行到下一步了,开始下一个模块的知识,DNS 解析,网络进程拿到 URL 进行 DNS 解析获取对应的 IP 地址和端口号。

DNS 是域名系统 (Domain Name System) 的缩写,提供的是一种主机名到 IP 地址的转换服务,就是我们常说的域名系统

2.1 DNS 解析流程

DNS 的缓存流程依次如下:

  1. 浏览器缓存【没错浏览器有缓存】

  2. 操作系统缓存【没错操作系统也有缓存】

  3. 本地的hosts文件【hosts文件,我们开发过程中常用到进行ip地址和域名的映射】

  4. 局域网/本地域名服务器

  5. 广域网域名服务器

  6. 根域名服务器

  7. 顶级域名服务器

  8. 权威域名服务器

  9. 返回 IP 地址或者解析出错

注意 DNS 解析的最终目的就是获取 IP 地址。

关于本地的 hosts 文件的配置,还有有点讲究的,可以参考一下这篇文章,不支持端口号,所以就导致我们能操作的范围很有限,比如我们起了两个服务分别是8080 和8081,那么就没有办法映射到两个域名了。

这个时候我们就可以用第三方工具,一个超级好用的抓包工具 whistle  可以很方便的实现hosts 文件的功能,而且操作起来更加方便。官网在这里

2.2 默认端口号

如果上述流程解析出来没有具体的端口号,那么 http 默认的端口就是80,https 的默认端口是443,注意这个是浏览器的网络进程在得到 dns 解析结果之后加上的端口,用于后续的请求。

websocket 的协议时 ws 和 wss ,默认端口号也是 80 和 443

2.3 获取目的 MAC 地址

IP 协议是网络层协议,还需要获取数据链路层通信使用的 MAC 地址,本机的 MAC 地址是发送源的 MAC 地址,目的 MAC 地址,可以使用 ARP 协议或者其他的协议获取。

ARP (address resolution protocol)是根据 IP 地址获取物理地址的一个 TCP / IP协议

三、发起 http 请求

DNS 解析完,获取了 IP 地址,然后又根据 ARP 协议获取物理 MAC 地址,接下来可以真正的发起网络请求了。

3.2 http 请求流程

浏览器的【网络进程】开始发起http请求,具体步骤如下:

  1. 应用层【也就是浏览器的网络进程】发起 http 请求

  2. 传输层 tcp 三次握手建立连接

  3. http 请求加上 tcp 头部,包括源端口号,目的端口号和用于校验数据完整性的序号,向下传输【这里的上下是计算机网络模型中抽象的上下】

  4. 网络层在数据包上加上 ip 头部,包括源 ip 地址和目的 ip 地址,向下传输

  5. 数据链路层 / 物理层 通过物理网络传输给服务器主机

  6. 服务器主机网络层收到数据包,解析 ip 头部,识别数据部分,向上传输

  7. 服务器主机传输层收到数据包,解析 tcp 头部,识别端口,识别数据部分,向上传输到应用层

  8. 服务器主机应用层 http 解析请求头和请求体,根据各种头部信息进行后续操作

    1. 301/302重定向,根据location字段重定向

    2. 304 使用缓存

    3. 200 成功,返回响应数据

  9. 响应数据原路返回应用层-传输层-网络层-网络层-传输层-应用层

  10. 数据传输完成,浏览器的网络进程收到响应数据

这里面有个面试题,网络的OSI 七层模型 和 五层模型分别是那些?

(1)ISO 组织制定的OSI七层模型分别是

  1. 应用层 (SMTP / FTP/ http / dns)
  2. 表示层
  3. 会话层 (tls /ssl)
  4. 传输层(tcp / udp )
  5. 网络层 (ip)
  6. 数据链路层( MAC 地址)
  7. 物理层

(2)tcp/ip的五层模型

  1. 应用层
  2. 传输层
  3. 网络层
  4. 数据链路层
  5. 物理层

3.3 TCP 三次握手

http 和 https 都是是基于 TCP 的请求,https 是 http 加上 tls 连接。TCP 是面向连接的协议。

对于 http1.1 协议 chrome 限制在同一个域名下最多可以建立 6 个 tcp 连接,所以如果在同一个域名下,同时有超过 6 个请求发生,那么多余的会进入排队等待状态,直到有请求完成。

http2 实现了多路复用,所有就不存在只能同时有 6 的 tcp 链接的限制了,对于 http1.1 和 http2 的区别,请参考这篇文章,http1.0、http1.1、http2、http3 的区别详解-CSD,N博客

浏览器的网络进程发起网络请求的第一步就是建立 TCP 连接了,这是个老生常谈的面试题,关于 TCP 的三次握手和断开连接的四次挥手。

这个三次握手背了那么多次都记不住?很难吗,其实就是3个请求而已,超级简单的啊,其中2个请求是客户端发起的,一个是服务端发起的,加起来一共3个,毫无难度的,而为 tcp 连接是浏览器发起的,也就是客户端发起的,所以客户端发起的请求肯定比服务端的多一个。

其实我们需要背诵的重点就是3个缩写单词,一个是SYN 同步标志,一个应答信号ACK,还有一个是 ISN 初始序列号,客户端和服务端都分别发送了同步标志和应答序列号

  1. 第一次握手(SYN):

    • 客户端向服务器发送一个带有 SYN(同步)标志的数据包,表示请求建立连接。
    • 客户端选择一个初始的序列号(ISN,Initial Sequence Number),并指定一些连接参数(如最大报文段大小)。
    • 状态变化:客户端进入 SYN-SENT 状态,等待服务器的响应。【已发送状态】
  2. 第二次握手(SYN + ACK):

    • 服务器接收到客户端的 SYN 请求后,向客户端回送一个带有 SYN 和 ACK(确认)标志的数据包,表示同意建立连接。
    • 服务器也选择一个自己的初始序列号,并确认客户端的 ISN。
    • 服务器还可以在这个阶段发送一些连接参数。
    • 状态变化:服务器进入 SYN-RECEIVED 状态。【已接受状态】
  3. 第三次握手(ACK):

    • 客户端接收到服务器的 SYN + ACK 响应后,向服务器发送一个带有 ACK 标志的数据包,表示已经收到服务器的确认。
    • 状态变化:客户端进入 ESTABLISHED 状态,服务器也进入 ESTABLISHED 状态。【已建立连接状态】
    • 此时,双方都已确认对方的能力,并可以开始进行数据传输。

3.5 https 中 tls 的握手

如果使用了 https 协议,那么在建立 tcp 连接之后,还会进行 tls 握手。也就是 https 的证书验证和密钥传输的过程。简化的流程如下:

  1. 客户端发送请求
  2. 服务端返回证书
  3. 客户端验证证书,提取公钥,生成对称加密的密钥,用公钥加密后发送给服务端
  4. 服务端收到请求,用私钥揭秘,得到对称加密的密钥,用密钥加密数据进行通信

关于https 的这个简化的 tls 握手逻辑可以配合 https 的中间人攻击流程进行理解,具体请参考这篇文章

为什么说他是简化的逻辑,因为还有很多逻辑,请参考这篇文章

3.6 返回数据

建立连接之后,服务端把相应的数据返回,返回的数据类型有很多中 html 文件、js 文件、图片等,涉及到浏览器渲染的,我们一般都是研究 html 文件。

一般来说我们打开开发者工具,第一个请求返回的就是 html 文件,他的类型是 document 

数据返回之后

  1. 如果使用http 1.0 短连接,那么此时 tcp 四次挥手断开链接

  2. 如果使用http 1.1 长连接,那么此时不会断开 tcp 连接,http1.1 的长连接是根据connection: keep-alive 头部字段指定的,长连接在长时间空闲的状态下会自动断开。

四、准备渲染

4.1 解析数据

在浏览器的网络进程收到服务端的返回的数据后,网络进程会对数据进行解析。根据 http 响应头的 content-type 来判断类型

  1. 如果是字节流,将将该请求交给下载管理器,结束导航流程  

    1. 比如输入一个浏览器不能预览的文件的地址,如:exe 文件,浏览器会直接下载这个文件

  2. 如果是 text/html 类型,进行下一步,通知【浏览器进程】获取文档准备渲染

也就是会所在网络进程收到第一个数据包的时候,浏览器进程就会开始准备渲染,而不是全部数据都接受完了才渲染。

4.2 通知渲染进程

浏览器进程收到消息之后,会判断当前页面 B 是否是已经存在的 A 页面打开的,并且和 A 是否是同一个站点,如果是,就复用 A 网页的渲染进程,否则创建一个新的渲染进程。

由此可见浏览器进程负责创建渲染进程,然后浏览器进程发出【提交文档】的消息给渲染进程。

注意,跨站和跨域的区别,跨站和跨域的区别-CSDN博客

4.3 渲染进程开始接受数据

请求是网络进程发起和接收的,所以目前为止,数据都在网络进程中,渲染进程需要和网络进程之间建立数据传输的管道。当数据传输完成后,渲染进程会返回【确认提交】的消息给浏览器进程。

4.4 浏览器进程更新页面状态

浏览器进程收到【确认提交】的消息后,更新浏览器的页面状态

  1. 更新安全状态,就是浏览器地址栏左边的一个锁的那个小图标

  2. 更新地址栏的url

  3. 更新前进、后退的历史状态

  4. 更新web页面,这个时候web页面是空白页

你自己测试一下,发现这会是一个很快的过程

五、渲染页面

接下来的工作就是【渲染进程】做的重点工作来,负责解析 html 文档,渲染用户可以看到的页面,主要流程可以看一下这篇文章,这里面学问可多着呢。

注意,接下来的逻辑都是针对 html 文档进行的。

5.1 获取文档

文档数据就是从网络进程传输过来的数据,不再赘述。

5.2 解析文档

这里面有两个关键的术语【词法分析】和【语法分析】,简单来说词法分析就是分析每个单词的词性,代表什么意思;语法分析就是分析单词组成的短语的意思,所有的编程语言的都需要相对应的编译系统来解释每一行代码的逻辑,但是这是底层逻辑了,我们可以先忽略。

5.2.1 构建 dom 树

重点是渲染进程通过一顿分析,会构建一个由 dom 节点组成的 dom 树。

5.2.2 构建 cssom 规则树

在解析过程中如果遇到 css 文件也会对 css 文件进程解析,构建相应的树形结构。

5.2.3 构建渲染树

根据 dom 树和 css 规则树构建渲染树,每个节点才是一个会被真实渲染到页面的对象。

dom 树中有一些节点不会在渲染树中

浏览器根据渲染树进行布局的过程就是回流,也就是布局/自动重排,我们很多时候都致力于减少重排提高性能就是发生在这步中的优化。

导致重排的操作:

  • 添加、删除、修改DOM元素。
  • 修改元素的尺寸、位置、边距、填充等。
  • 改变窗口大小。
  • 修改页面默认字体。

最后一步就是绘制阶段,把渲染树绘制在屏幕上,我们很多时候说的重绘就是这一步中的概念。

导致重绘的操作:

  • 修改元素的颜色、背景色、文字样式等。
  • 使用CSS伪类(如 :hover)引起的样式变化。
  • 添加或移除样式表。

总结下来就是5个步骤

  1. 生成dom 树
  2. 生成 css 规则书
  3. 生成渲染树
  4. 回流
  5. 绘制

5.3 阻塞问题

这是在渲染页面过程中最常出现的问题,问你 css 会不会阻塞?script 会不会阻塞?别蒙圈了,很简单,我们首先来说 js

5.3.1 script 标签

(1)html 中

script 标签一般由两种一种是写在 html 中的,如下

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=<device-width>, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    test
    <script>
        console.log(1)
    </script>
</body>
</html>

这种 js 的加载、解析和执行都会阻塞文档的解析,所以一般我们把 script 标签放在 body 最后。阻塞的意思是渲染进程停止了dom 解析,开始执行 js 代码,这样是很影响用户体验的。

(2)引用外部资源

还有一种是使用 src 属性引用外部资源,有两个可以配置的属性 defer 和 async 。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=<device-width>, initial-scale=1.0">
    <title>Document</title>
    <script src="xxx.js"></script>
    <script defer src="xxx2.js"></script>
    <script async src="xxx3.js"></script>
</head>
<body>
    test
</body>
</html>

script 标签可以设置 defer 或 async 属性:

(1)aync: 异步加载,加载后立即执行【在执行的时候如果 dom 没有解析完,就需要停下来,所以也是会阻塞dom的解析的】【如果 dom 解析完了,需要使用脚本中的变量,这个时候可能获取不到,因为可能脚本还没加载完】,多个带 async 属性的标签,不能保证加载的顺序;

(2)defer: 异步加载,加载完之后等 dom 解析完再执行,也就是说不会阻塞 dom 的解析,多个带defer 属性的标签,按照顺序执行。

(3)如果不配置这两个属性,那么毫无疑问就是会阻塞 dom 的解析和渲染

这样看来 defer 属性更加好一点,不会阻塞解析,但是具体怎么用还是看具体使用的场景。

5.3.2 css 文件/代码

css 有种使用方式

(1)内联样式

html 头部 style标签中的内联样式,没有额外的资源加载,直接解析生成 cssom 规则树,不存在阻塞 dom 解析的问题。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=<device-width>, initial-scale=1.0">
    <title>Document</title>
    <style>
        a {
            color: red;
        }
    </style>
</head>
<body>
    test
</body>
</html>
(2)link 标签

link 标签的 href 地址指向一个文件地址,需要去加载资源,他一般是不会阻塞解析的,但是会阻塞渲染,因为他就是一个链接,在解析 dom 的时候用不到,在渲染的时候却会用到 。浏览器会异步下载 link 指定的 css 文件。

但是,如果 html 文档中有脚本,而脚本中有获取 css 样式的代码,css 若没有加载完,就会报错,所以浏览器此时会等待 cssom 树构建完再执行脚本,最后继续再解析文档,只有此时是会阻塞 dom 的解析了。此时 link 影响力 js 脚本的执行,js 脚本影响了 dom 的解析。

总结:一般不阻塞dom解析,但是会阻塞渲染,当脚本使用 link 中样式信息是,会阻塞脚本执行,进而阻塞dom解析和渲染

参考这篇文章, 注意 href 和 src的区别。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=<device-width>, initial-scale=1.0">
    <title>Document</title>
    <link rel="stylesheet" type="text/css" href="xxx.css"/>
</head>
<body>
    test
</body>
</html>
(3)@import

@import 会等到页面全被下载完之后再下载 impor t的内容,@import不会阻塞页面的加载和渲染,但是如果import的文件加载时间过长会产生样式闪烁的问题!

5.4 页面性能指标

在整个过程还会涉及到性能指标的问题,可以参考这篇文章。

前端web页面性能的指标有哪些?-CSDN博客

六、断开连接

虽然 http1.1 是长连接【使用 keep-alive 字段】,但是为了不占用资源,也会在空闲的时候自动断开tcp 连接,一般是 300s 后。参考这篇文章

6.1 TCP 四次挥手

TCP 通过四次挥手优雅的关闭连接

四次挥手也很好记,4/2 = 2 一遍两个,客户端发送2个请求,服务端发送2个请求。

在这个步骤我们也只需要记住两个单词,一个是 FIN 代表finish 标注的数据包,一个是ACK应答数据包

  1. 第一次挥手(FIN):

    • 客户端或应用发起关闭连接的请求,向服务器发送一个带有 FIN(Finish)标志的数据包。
    • 状态变化:发起关闭的一方进入 FIN-WAIT-1 状态,表示已经完成数据的发送。
    • 简洁说法:客户端请求关闭连接,发送第一个带有结束标志的数据包】
  2. 第二次挥手(ACK):

    • 服务器接收到带有 FIN 标志的数据包后,向客户端发送一个确认(ACK)数据包,表示已经收到了关闭请求。
    • 服务器也可以在这个阶段继续发送数据。
    • 状态变化:服务器进入 CLOSE-WAIT 状态。
    • 简洁说法:服务端收到客户端关闭连接的请求,回复一个确认数据包】
  3. 第三次挥手(FIN):

    • 服务器准备好关闭连接时,向客户端发送一个带有 FIN 标志的数据包,表示服务器已经完成数据的发送。
    • 状态变化:服务器进入 LAST-ACK 状态,等待客户端的最后一个 ACK。
    • 简洁说法:服务端请求关闭连接,发送第一个带有结束标志的数据包】
  4. 第四次挥手(ACK):

    • 客户端接收到服务器的关闭请求后,向服务器发送一个确认(ACK)数据包,表示已经收到了关闭请求。
    • 状态变化:客户端进入 TIME-WAIT 状态,等待可能出现的延迟的数据包。在这个状态等待一段时间后,客户端关闭连接。
    • 简洁说法:客户端收到服务端关闭连接的请求,回复一个确认数据包】

在这个过程中,TIME-WAIT 状态的目的是确保已发送的 ACK 能够被对方接收。TIME-WAIT 状态的时间等于两个最大报文段生存时间(2MSL,Maximum Segment Lifetime),通常是 2 分钟,以确保所有可能的延迟数据包都已经消失。这样可以防止连接中的旧数据包对新连接造成干扰。

6.2 TCP 的机制

6.2.1 重传机制 

  1. 基于时间的超时重传
    1. 当发送方发送一个数据段后,会启动一个定时器(Timer)。如果在定时器到期之前没有收到对应的确认(ACK),发送方就会认为数据段丢失,然后重新发送该数据段。
    2. 超时时间的选择是一个重要的参数。如果超时时间设置得太短,可能会导致不必要的重传;如果设置得太长,可能会延迟检测丢失。TCP的超时时间通常是根据网络的往返时间(Round-Trip Time,RTT)和网络拥塞情况动态调整的。
  2. 基于确认的快速重传
    1. 如果发送方连续收到三个相同的确认(ACK),就会认为对应的数据段丢失,触发快速重传机制。在这种情况下,发送方不等待超时,而是立即重传丢失的数据段。
    2. 这是基于一种假设,即如果接收方已经收到了后续的数据段,那么之前的丢失的数据段也很可能已经到达接收方,只是确认丢失了

重传机制就可以解答为什么四次挥手的时候,客户端在第四个客户端发送的 确认数据包之后,需要等待 2个最大报文生存时间,再关闭连接,因为要确认服务端已经接收到了确认应答,如果服务端没接收到,一定会重新发送【请求关闭连接】的报文。

tcp 的重传机制保证了tcp 的可靠性,这也是和 upd 的重要区别之一

6.2.2 拥塞控制机制

  1. 慢启动
  2. 拥塞避免
  3. 快速重传
  4. 快速恢复
  5. 超时重传
  6. 拥塞标记

6.2.3 流量控制机制

滑动窗口协议

6.2.4 可靠传输机制

基于连续 ARQ 协议和滑动窗口协议

注意是 ARQ,不是 ARP,ARP 是获取 MAC 地址的协议

6.3  TCP 和 UDP 

具体可以参考这篇文章

  • 20
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值