一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第 1 天,点击查看活动详情。
🧨 大家好,我是 Smooth,一名大二的 SCAU 前端er
🏆 本篇文章是我在复习该知识点时,查看了多篇文章之后,结合我自己的个人思考与理解汇聚而成的精华
🙌 如文章有误,恳请评论区指正,谢谢!
❤ 写作不易,「点赞」+「收藏」+「转发」 谢谢支持!
正文开始前你需知道的
从输入URL到页面显示不仅仅是一道经典的面试题,这个知识点中涉及到了非常广泛的知识点,因此面试官可以通过这个问题有点到面的向候选人发起提问,来看看面试者在计算机网络和浏览器这方面知识的广度和深度。
废话不多说,下面就开始吧!
文章目录一览
- 解析用户输入内容(关键字还是 URL)
- 看是否命中强缓存(没有则继续下一步)
- DNS 解析(找到服务器的 IP 地址)
- 建立 TCP 连接(三握)
- 发送 HTTP 请求(先看是否命中协商缓存,有则 304,没有则 200 和新资源)并接收对应响应报文
- 关闭 TCP 连接
- 提交文档阶段
- 浏览器渲染
一、用户输入阶段
用户在地址栏输入内容之后,浏览器会首先判断用户输入的是合法的URL还是搜索内容,如果是搜索内容就合成URL,如果是合法的URL就开始进行加载。
在这里,要明确我们具体要查找的东西到底是什么
搜索关键字
我们要搜索一些内容,例如去百度搜索引擎进行百度查找某个问题的答案,那么浏览器就会自动调用默认搜索引擎进行关键字的查找
-
这个流程需要对输入的不安全字符编码进行转义(安全字符指的是数字、英文和少数符号),使用的编码是 UTF-8 编码。因为URL的参数是不能有中文的,也不能有一些特殊字符,比如
= ? &
,否则当我搜索1+1=2
,假如不加以转义,url会是/search?q=1+1=2&source=chrome
,和URL本身的分隔符=
产生了歧义。 -
URL对非安全字符转义时,使用的编码叫百分号编码,因为它使用百分号加上两位的16进制数表示。这两位16进制数来自UTF-8编码,将每一个中文转换成3个字节,比如我在google地址栏输入“中文”,url会变成
/search?q=%E4%B8%AD%E6%96%87
,一共6个字节。 -
我们在写代码时经常会用的
encodeURI
和encodeURIComponent
正是起这个作用的,它们的规则基本一样,只是= ? & ; /
这类URI组成符号,这些在encodeURI
中不会被编码,但在encodeURIComponent
中统统会。因为encodeURI
是编码整个URL,而encodeURIComponent
编码的是参数部分,需要更加严格把关。
搜索关键字阶段参考的是 @前端私教年年 大佬的这篇文章,链接在此
域名解析对应的 IP
如果是合法的 URL,说明我们想通过该 URL(统一资源定位符) 查找到对应服务器上的该资源,那么要查找的实质就是 该域名解析后所对应的 IP 地址,即 域名与 IP 地址的一个映射关系。
后面篇幅重点讲到的都是第二种情况,后续篇幅中我将其简称为 映射关系
二、查找强制缓存
在有效期内的缓存资源直接使用,称之为强缓存,从 chrome 网络面板看到这类请求直接返回200,size 是 memory cache
或者 disk cache
。memory cache
是指从资源从内存中被取出,disk cache
是指从磁盘中被取出;从内存中读取比从磁盘中快很多,但资源能不能分配到内存要取决于当下的系统状态。通常来说,刷新页面会使用内存缓存,关闭后重新打开会使用磁盘缓存。
-
浏览器首次加载资源成功时,服务器返回 200,此时浏览器不仅将资源下载下来,而且把 response 的 header(里面的 date 属性非常重要,用来计算第二次相同资源时当前时间和 date 的时间差)一并缓存;
-
下一次加载资源时,首先要经过强缓存的处理。强缓存是利用 http 头中的 Expires 和 Cache-Control 两个字段来控制的。强缓存中,当请求再次发出时,浏览器会根据其中的 Expires 和 Cache-Control 判断目标资源是否“命中”强缓存,若命中则直接从缓存中获取资源,不会再与服务端发生通信。其中
Cache-Control
的优先级最高,如果Cache-Control:no-cache
,就直接进入到协商缓存的步骤了,如果Cache-Control:max-age=xxx
,就会先比较当前时间和上一次返回 200 时的时间差,如果没有超过 max-age,命中强缓存,不发请求直接从本地缓存读取该文件(这里需要注意,如果没有Cache-Control
,会取Expires
的值,来对比是否过期),过期的话会进入下一个阶段,协商缓存,这个后面再提到。
三、DNS 解析
DNS 解析目的是找到目标服务器的 IP 地址,才能进行后续的协商缓存验证或真实的从服务器拿到所需数据
-
先找浏览器本地的缓存
-
找操作系统 hosts 文件的缓存
-
以上两步都没找到需要的资源,就要进行网络请求啦,开始跟外界打交道,去本地 DNS 服务器(local DNS)进行查找,如果本地 DNS 服务器的缓存中已经存在该映射关系,那么直接返回,否则继续进行后续步骤(99% 的 DNS 解析到这一步就结束了,因为一般
local DNS
中存在大量映射关系缓存)。 -
本地 DNS 服务器作为代理服务器,向它上面的根域名服务器建立 UDP 连接后发出请求,根域名服务器返回查询域的主域名服务器(即
gTLD
,像.COM
、.CN
这种顶级域名)。 -
本地 DNS 服务器拿到后向
gTLD
发请求,gTLD
是可以找到你想查找域名的Name Server
地址的,本地 DNS 服务器向Name Server
地址发送请求,拿到该域名对应的 IP 和 TTL(time to live,即域名解析结果在DNS服务器中存活的时间),然后将结果先是自己做一个缓存(根据 TTL 设置映射存活时间),然后返回给浏览器,DNS 解析结束
Name Server 概念
服务商提供的服务器地址,比如你在阿里云注册的域名,那查找就是去阿里云的服务器查,因为你是去域名提供商的服务器发请求,那肯定是能拿到对应的ip地址的
四、建立 TCP 连接
首先会等待 TCP 队列
chrome 有个机制,同一个域名同时最多只能建立6个TCP连接,如果超过这个数量的连接必须要进入排队等待状态。
知识巩固
对于多路复用,http 1.1 采取建立多个 TCP 连接,http 2.0 采用建立一个 TCP 连接并行发起多个请求
正式开始建立 TCP 连接
通过TCP三次握手与服务器建立连接,然后进行数据传输。
第一次握手: 建立连接。客户端发送连接请求报文段,将SYN位置为1,Sequence Number为x;然后,客户端进入SYN_SEND状态,等待服务器的确认;
第二次握手: 服务器收到SYN报文段。服务器收到客户端的SYN报文段,需要对这个SYN报文段进行确认,设置Acknowledgment Number为x+1(Sequence Number+1);同时,自己自己还要发送SYN请求信息,将SYN位置为1,Sequence Number为y;服务器端将上述所有信息放到一个报文段(即SYN+ACK报文段)中,一并发送给客户端,此时服务器进入SYN_RECV状态;
第三次握手: 客户端收到服务器的SYN+ACK报文段。然后将Acknowledgment Number设置为y+1,向服务器发送ACK报文段,这个报文段发送完毕以后,客户端和服务器端都进入ESTABLISHED状态,完成TCP三次握手。
A:我要跟你建立连接,你那边接受得到我的请求吗?
B:接收到啦!没问题,但得向你确认下,证明这是你真实的要准备跟我建立的连接,而不是你很久之前发过的
A:对,没问题,这是我刚发给你的
为什么要三次握手?两次可以吗?
为了防止已失效的连接请求报文段突然又传送到了服务端,因而产生错误。
具体例子如下
已失效的连接请求报文段 的产生在这样一种情况下:client 发出的第一个连接请求报文段并没有丢失,而是在某个网络结点长时间的滞留了,以致延误到连接释放以后的某个时间才到达 server。本来这是一个早已失效的报文段。但 server 收到此失效的连接请求报文段后,就误认为是 client 再次发出的一个新的连接请求。于是就向 client 发出确认报文段,同意建立连接。假设不采用“三次握手”,那么只要server发出确认,新的连接就建立了。由于现在 client 并没有发出新的建立连接请求,因此不会理睬 server 的确认,也不会向 server 发送数据。但 serve r却以为新的运输连接已经建立,并一直等待 client 发来数据。这样,server 的很多资源就白白浪费掉了。采用“三次握手”的办法可以防止上述现象发生。例如刚才那种情况,client 不会向 server 的确认发出确认。server 由于收不到确认,就知道 client 并没有要求建立连接。”
四次握手可以吗?
可以但没必要
四次握手只是将第二次握手分解为了以下两个步骤
- 先发送确认位 ACK = 1,然后确认号 ack = x + 1
- 再发送同步位 SYN = 1,和序号 seq = y
五、发送 HTTP 请求并接收相应结果
先看是否命中协商缓存
- 有则返回状态码 304 并更新资源标识符
- 没有则返回状态码 200 和新资源
超过有效期的,则携带缓存的资源标识向服务端发起请求,校验是否能继续使用,如果服务端告诉我们,可以继续使用本地存储,则返回 304,并且不携带数据;如果服务端告诉我们需要用更新的资源,则返回 200,并且携带更新后的资源和资源标识缓存到本地,方便下一次使用。
然后接收对应响应报文
服务器首先返回相应行,包括协议版本和状态码,然后会返回响应头包含返回的数据类型,服务器要在客户端保存的 Cookie
等,一般来说是返回 HTML
、CSS
、JS
、Image
文件
对于是否命中协商缓存
-
协商缓存阶段,则向服务器发送 header 带有 If-None-Match 和 If-Modified-Since 的请求,前者优先级大于第二个,所以先讲第一个对应关系 请求头的
If-None-Match
和 响应头的Etag
。服务器会比较这两个字段值,如果相同,命中协商缓存,返回 304;如果不一致则有改动,直接返回新的资源文件带上新的Etag
值并返回 200。 -
协商缓存第二个对应关系是
If-Modified-Since
和Last-modified
,如果客户端发送的 If-Modified-Since 的值跟服务器端获取的文件最近改动的时间,一致则命中协商缓存,返回 304;不一致则返回新的Last-modified
和文件并返回 200;
为什么协商缓存中 Etag 优先级大于 Last-modified ?
因为后者是标识着资源的最后修改时间(不准确,因为修改了文件并不代表文件内容发生改变,可能改变后又撤销了),前者可以理解为是一个资源唯一标识符,是服务器通过内置算法根据文件内容生成的 hash
值,所以更准确,当然,因为计算所以性能消耗更大,不太推荐使用 Etag
拓展知识
什么是启发式缓存,在什么条件下触发?
启发式缓存:
如果响应中未显示 Expires
,Cache-Control:max-age
或 Cache-Control:s-maxage
,并且响应中不包含其他有关缓存的限制,缓存可以使用启发式方法计算新鲜度寿命。通常会根据响应头中的2个时间字段 Date
减去 Last-Modified
值的 10% 作为缓存时间。
// Date 减去 Last-Modified 值的 10% 作为缓存时间。
// Date:创建报文的日期时间, Last-Modified 服务器声明文档最后被修改时间
response_is_fresh = max(0,(Date - Last-Modified)) % 10
看到这里是不是感觉有点累了?坚持住!后面还有更丰富的知识等着你去探索!
六、关闭 TCP 连接
数据传输完成后,通过四次挥手来断开连接。
当客户端和服务器通过三次握手建立了TCP连接以后,当数据传送完毕,肯定是要断开TCP连接的啊。那对于TCP的断开连接,这里就有了神秘的“四次挥手”。
第一次挥手: 主机1(可以使客户端,也可以是服务器端),设置Sequence Number,向主机2发送一个FIN报文段;此时,主机1进入FIN_WAIT_1状态;这表示主机1没有数据要发送给主机2了;
第二次挥手: 主机2收到了主机1发送的FIN报文段,向主机1回一个ACK报文段,Acknowledgment Number为Sequence Number加1;主机1进入FIN_WAIT_2状态;主机2告诉主机1,我“同意”你的关闭请求;
第三次挥手: 主机2向主机1发送FIN报文段,请求关闭连接,同时主机2进入LAST_ACK状态;
第四次挥手: 主机1收到主机2发送的FIN报文段,向主机2发送ACK报文段,然后主机1进入TIME_WAIT状态;主机2收到主机1的ACK报文段以后,就关闭连接;此时,主机1等待2MSL后依然没有收到回复,则证明Server端已正常关闭,那好,主机1也可以关闭连接了。
A:我要断开连接啦
B:好的,你先断开,等我把我这边的数据传完给你我再断
过了一会儿,等B传完后
B:我传完啦,我也可以断开跟你的连接了,听到了吗
A:知道你也断开连接啦,你先断,我过 2MSL 再断,不然怕你听不见我跟你说的话
为什么要四次挥手?
TCP 协议是一种面向连接的、可靠的、基于字节流的运输层通信协议。TCP 是全双工模式,这就意味着,当主机1发出FIN 报文段时,只是表示主机1已经没有数据要发送了,主机1告诉主机2,它的数据已经全部发送完毕了;但是,这个时候主机1还是可以接受来自主机2的数据;当主机2返回 ACK 报文段时,表示它已经知道主机1没有数据发送了,但是主机2还是可以发送数据到主机1的;当主机2也发送了 FIN 报文段时,这个时候就表示主机2也没有数据要发送了,就会告诉主机1,我也没有数据要发送了,之后彼此就会愉快的中断这次TCP连接。
为什么要等待 2MSL?
MSL:报文段最大生存时间,它是任何报文段被丢弃前在网络内的最长时间。 原因有二:
- 保证TCP协议的全双工连接能够可靠关闭
- 保证这次连接的重复数据段从网络中消失
第一点:如果主机1直接 CLOSED 了,那么由于 IP 协议的不可靠性或者是其它网络原因,导致主机2没有收到主机1最后回复的 ACK。那么主机2就会在超时之后继续发送 FIN,此时由于主机1已经 CLOSED 了,就找不到与重发的 FIN 对应的连接。所以,主机1不是直接进入 CLOSED,而是要保持 TIME_WAIT,当再次收到FIN的时候,能够保证对方收到 ACK,最后正确的关闭连接。
第二点:如果主机1直接 CLOSED,然后又再向主机2发起一个新连接,我们不能保证这个新连接与刚关闭的连接的端口号是不同的。也就是说有可能新连接和老连接的端口号是相同的。一般来说不会发生什么问题,但是还是有特殊情况出现:假设新连接和已经关闭的老连接端口号是一样的,如果前一次连接的某些数据仍然滞留在网络中,这些延迟数据在建立新连接之后才到达主机2,由于新连接和老连接的端口号是一样的,TCP 协议就认为那个延迟的数据是属于新连接的,这样就和真正的新连接的数据包发生混淆了。所以 TCP 连接还要在 TIME_WAIT 状态等待2倍 MSL,这样可以保证本次连接的所有数据都从网络中消失。
关于三握四挥的内容,在谢希仁的《计算机网络》一书中讲的非常透彻,有时间的可以进行查阅
七、提交文档阶段
-
网络进程将获取的数据进行解析,根据响应头中的 Content-type 来判断响应数据的类型,如果是字节流类型,就将该请求交给下载管理器去下载,如果是 text/html 类型,就通知浏览器进程获取到的是 HTML,准备渲染进程。
-
一般情况下浏览器的一个 tab 页面对应一个渲染进程,如果从当前页面打开的新页面并且属于同一站点,这种情况会复用渲染进程,其他情况则需要创建新的渲染进程。
-
渲染进程准备好之后,浏览器会发出提交文档的消息给渲染进程,渲染进程收到消息后,会和网络进程建立数据传输的管道(IPC),文档数据传输完成后,渲染进程会返回确认提交的消息给浏览器进程。
-
浏览器收到确认提交的消息后,会更新浏览器的页面状态,包括了安全状态,地址栏的URL,前进后退的历史状态,并更新 web 页面为空白。
有需要的小伙伴可以了解一下 安全沙箱 和 站点隔离 的概念
八、浏览器渲染阶段
前置知识
-
在浏览器获取到 HTML 文件后,由于无法直接理解与使用 HTML,所以要先将他转换为能够读懂的 DOM 树结构
-
CSS 文件一样没法被浏览器直接理解,所以首先把 CSS 解析成样式表。这三类样式都会被解析:
- 通过 link 引用的外部 CSS 文件(外联)
<style>
标签内的样式(内联)- 元素的 style 属性内嵌的 CSS(行内样式)
开始介绍
- 渲染进程将 HTML 内容转换为能够读懂的 DOM 树结构
- 渲染引擎将 CSS 样式表转化为浏览器可以理解的 styleSheets,并计算出 DOM 节点样式后,生成 CSS 树
- 浏览器根据 DOM 树与 CSS 树生成 render 树(渲染树)
- 布局阶段(layout):浏览器根据 render 树生成 layout tree(布局树),在布局树中,DOM 树上所有不可见节点都没加上去(比如 display: none 的元素)。layout tree 上会显示出各个节点的坐标位置以及宽高
- 绘制阶段(painting):接着会根据 layout tree 生成 layer tree(图层树),它是根据你的不同节点所对应的层级关系来生成的。并根据你的 layer tree 去生成 painting 表,它记录了绘制图层的指令和步骤。
- 光栅化操作:此时会将 paint 表 commit 到渲染进程的合成线程中去操作。
- 接着浏览器会合并合各个图层,将数据由 CPU 输出给 GPU 最终绘制在屏幕上。(复杂的视图层会给这个阶段的 GPU 计算带来一些压力,在实际应用中为了优化动画性能,我们有时会手动区分不同的图层)。
- 最后,合成完后,合成线程会发送一个指令给浏览器,告诉它已完成,让浏览器显示页面
至此,我们已经分析完了整个渲染流程,从 HTML 到 DOM、样式计算、布局、分层、绘制、光栅化、合成和现实
该阶段推荐阅读此文章,更详细,点击跳转
关于页面渲染阻塞
由于浏览器解析并渲染 DOM 元素占用的是主线程,对于图片、CSS 等文件在下载时不会阻塞浏览器渲染,但 JS 就不一样了,浏览器遇到 <script>
标签引入的 JS 时,会停止渲染,等 JS 文件下载并执行完后,才将主线程控制器归还给浏览器进行继续渲染
导致的问题
由上面描述可知,浏览器渲染(布局和绘制) 和 JS 文件的处理都要占用主线程,但如果 JS 文件特别大,下载和执行占用的时间非常长,我们都知道一个知识点,当页面以每秒60帧的频率刷新,才不会让用户感受到页面卡顿。在浏览器要绘制下一帧页面时,无法及时将浏览器主线程控制权归还,那么就会导致无法及时绘制下一帧的问题,这时对于我们用户来说的直观感受就是,出现页面卡顿。
每一帧绘制结束后,还有剩余时间
当 JS 过大
有什么解决方案呢?
可以先自己思考,过一段时间后再去查看后面的 RQ1 获得解决方案
关于浏览器渲染过程,推荐观看 此视频
对于上述八大阶段的一些补充
如果在建立 TCP 连接后,有安全传输需求的,那就要建立 https 安全层,即进行 TLS 握手
协商加密密钥——TLS握手
为了保障通信的安全,我们使用的是 HTTPS 协议,其中的 S 指的就是 TLS。TLS 使用的是一种 非对称 + 对称 的方式进行加密。
-
对称加密就是两边拥有相同的秘钥,两边都知道如何将密文加密解密。这种加密方式速度很快,但是问题在于如何让双方知道秘钥。因为 传输数据都是走的网络,如果将秘钥通过网络的方式传递的话,秘钥被截获,就失去了加密的意义。
-
非对称加密,每个人都有一把公钥和私钥,公钥所有人都可以知道,私钥只有自己知道,将数据用公钥加密,解密必须使用私钥。这种加密方式就可以完美解决对称加密存在的问题,缺点是速度很慢。
我们采取非对称加密的方式协商出一个对称密钥,这个密钥只有发送方和接收方知道的密钥,流程如下:
- 客户端发送一个随机值以及需要的协议和加密方式;
- 服务端收到客户端的随机值,发送自己的数字证书,附加上自己产生一个随机值,并根据客户端需求的协议和加密方式使用对应的方式;
- 客户端收到服务端的证书并验证是否有效,验证通过会再生成一个随机值,通过服务端证书的公钥去加密这个随机值并发送给服务端;
- 服务端收到加密过的随机值并使用私钥解密获得第三个随机值,这时候两端都拥有了三个随机值,可以通过这三个随机值按照之前约定的加密方式生成密钥,接下来的通信就可以通过该对称密钥来加密解密;
通过以上步骤可知,在 TLS 握手阶段,两端使用非对称加密的方式来通信,但是因为非对称加密损耗的性能比对称加密大,所以在正式传输数据时,两端使用对称加密的方式。
问题汇总
A & Q1:浏览器渲染卡顿的解决方法
1. 可以通过 requestAnimationFrame()
来解决
这是浏览器的官方 API,此方法会在每一帧被调用,通过 API 的回调,我们可以把 JS 运行任务分成一些更小的任务快(分到每一帧),在每一帧时间用完前暂停 JS 执行,归还主线程,这样的话在下一帧开始时,主线程就可以按时执行布局和绘制
React 最新的渲染引擎 React Fiber 就是用到了这个 API 做了很多优化(时间分片)
2. 为 <script>
标签加上 async
或 defer
属性
-
async
:异步执行,当浏览器在渲染时,如果遇到<script>
对应的JS
文件可同时进行文件下载与浏览器渲染,即并行。当下载结束后,立刻停止浏览器渲染,执行完 JS 后,再继续渲染 -
defer
:延迟执行,如果遇到<script>
对应的JS
文件也可同时进行文件下载与浏览器渲染,即并行。只不过下载结束后,不会立刻停止浏览器渲染,而是等浏览器全部页面布局绘制完成,才执行 JS 文件
3. 这里就要提到 CSS 中的一个动画属性 transform
了
由于栅格化的整个流程是不占用主线程的,只在下面的 合成器线程 + 栅格线程中执行,意味着它无需和 JS 抢夺主线程,我们如果反复进行重排和重绘,可能会导致掉帧,这是因为有可能 JS 执行阻塞了主线程,而经 transform
实现的动画不会经过布局和绘制,而是直接运行在合成器线程和栅格线程,所以不会受到主线程中 JS 执行的影响,所以节省了很多时间,减轻了主线程的压力。
A & Q2:CSSOM 渲染会不会阻塞 DOM 渲染? 会不会阻塞 DOM 树建立?
会,不会,即构建 CSSOM 会阻塞 DOM 渲染,不会阻塞 DOM 树建立
-
CSSOM 和 DOM 树的构建都是渲染进程干,并行执行的,
-
只有上面两个 tree 构建完成才能开始构建 render tree,再经过布局、分层、绘制、合成,最后 DOM 渲染。
不能说是阻塞,而是先后顺序的问题
浏览器内核拓展
上文提到页面渲染是渲染进程的任务,这个渲染进程中又细分为 GUI 渲染线程和 JS 线程。
-
解析 HTML 生成 DOM 树,解析 CSS 生成样式表以及后面去生成布局树、图层树都是由 GUI 渲染线程去完成的,这个线程可以一边解析 HTML,一边解析 CSS,这两个是不会冲突的,所以也提倡把CSS在头部引入。
-
但是在 JS 线程执行时,GUI 渲染线程没有办法去解析 HTML,这是因为 JS 可以操作 DOM,如果两者同时进行可能引起冲突。如果这时 JS 去修改了样式,那此时 CSS 的解析和 JS 的执行也没法同时进行了,会先等 CSS 解析完成,再去执行 JS,最后再去解析 HTML。
类似于 JS 的 async、defer 优化
就像 async 或 defer 属性使 script 元素不阻塞解析一样,外部的样式表也可以通过 media 属性 使其不阻塞渲染。使用 media 属性值,浏览器可以智能地决定何时去加载样式表
A & Q3:页面渲染优化的方法
- HTML文档结构层次尽量少,最好不深于六层;
- 脚本尽量后放,放在前即可;
- 少量首屏样式内联放在标签内;
- 样式结构层次尽量简单;
- 在脚本中尽量减少DOM操作,尽量缓存访问DOM的样式信息,避免过度触发回流;
- 减少通过JavaScript代码修改元素样式,尽量使用修改class名方式操作样式或动画;
- 动画尽量使用在绝对定位或固定定位的元素上;
- 隐藏在屏幕外,或在页面滚动时,尽量停止动画;
- 尽量缓存DOM查找,查找器尽量简洁;
- 涉及多域名的网站,可以开启域名预解析
A & Q4:强缓存和协商缓存发生在那个阶段?
强缓存和协商缓存发生在发起 URL 请求阶段,在这个阶段构建请求行之后会查找缓存。
A & Q5:DNS解析中端口需要DNS解析吗?
不需要,因为 HTTP 默认的是80端口,HTTPS 默认的是443端口,如果要指定端口可以直接在 URL 里面添加。
A & Q6:上述哪些阶段可以优化?
1. 优化 DNS 查询:DNS 预解析
前端的 DNS
优化,可以在 html
页面头部写入 DNS
缓存地址,比如
<meta http-equiv="x-dns-prefetch-control" content="on" />
<link rel="dns-prefetch" href="http://bdimg.share.baidu.com" />
2. 优化TCP连接:可以通过请求头 keep-alive 来优化。
keep-alive: connection
在本次 TCP 请求结束后,不主动断开,下次还需要 TCP 连接时就省了,直接在这个 TCP 管道进行传输即可
即节省了进行三次握手的建立 TCP 连接过程
3. 优化 HTTP 响应报文:通过 CDN 和 Gzip 压缩。
A & Q7:常见 http 请求报文头有哪些?
User-Agent
浏览器类型Content-Type
报文类型Connection
完成传输是否关闭 TCP 连接Host
访问主机域名Content-Length
内容长度Accept
允许接收的数据类型Accept-Language
允许接收的语言Cookie
用户标识符cache-control
强缓存限制条件,存活时间expires
强缓存限制条件,被上面的替代了etag
协商缓存限制条件,过期的时刻last-modified
协商缓存限制条件,最后修改时间date
时间
等等,还有很多,就不一一列举了
最后
我是 Smoothzjc,致力于产出更多且不仅限于前端方面的优质文章
大家也可以关注我的公众号 @ Smooth前端成长记录,及时通过移动端获取到最新文章消息!
写作不易,「点赞」+「收藏」+「转发」 谢谢支持❤
往期推荐
《都2022年了还不考虑来学React Hook吗?6k字带你从入门到吃透》
《一份不可多得的 Webpack 学习指南(1万字长文带你入门 Webpack 并掌握常用的进阶配置)》
《通过 React15 ~ 17 的优化迭代来简单聊聊 Fiber》
《【offer 收割机之手写系列】10分钟带你掌握原理并手写防抖与节流的立即/非立即执行版本》
《【offer 收割机之 CSS 回顾系列】请你解释一下什么是 BFC ?他的应用场景有哪些?》
《Github + hexo 实现自己的个人博客、配置主题(超详细)》
《【建议收藏】长达万字的git常用指令总结!!!适合小白及在工作中想要对git基本指令有所了解的人群》
《浅谈javascript的原型和原型链(新手懵懂想学会原型链?看这篇文章就足够啦!!!)》