从一道面试题说起--HTTP、TCP、浏览器缓存(协商缓存、强缓存、缓存位置)、DNS查询、浏览器渲染原理(前端)

294 阅读12分钟

前端部分--计算机网络

在浏览器的地址栏输入URL到返回页面会发生什么,这个问题可以说一分钟,也可以说半个小时,你说了一分钟,面试官肯定会追问你,那么今天就来梳理下这道题。

生成HTTP请求报文

1.URL的作用是什么

我们在浏览器中输入URL,目的就是请求某台服务器上的某个资源(通俗理解为文件),URL就要唯一标识这个资源的存放地址。所以,URL的组成如下:

https://www.example.com:8080/path/to/resource?query=value#fragment

协议:指定用于访问资源的协议,例如HTTP或HTTPS。

域名:指定资源所在服务器的域名,例如www.example.com。

端口(可选):指定服务器上用于访问资源的端口号。未指定则使用协议的默认端口号。

路径:指定资源在服务器上的位置。

查询字符串(可选):包含一组键值对,用于向服务器传递额外信息。

片段标识符(可选):指定资源中的一个特定部分。

2.解析URL,生成HTTP请求报文

HTTP请求报文由以下几个部分组成:请求行、请求头、请求体。

请求行

请求方法(GET POST)、URL、协议版本号(HTTP1.1)

请求头

一个一个的K-V键值对,常见的有以下几个:

前面是new Bing的结果,后面加粗的是我总结的考点

  • Accept:指定客户端能够接受的响应类型。
  • Accept-Encoding:指定客户端能够接受的响应编码方式。
  • Cache-Control:指定缓存控制指令。 后面说,浏览器缓存
  • Connection:指定连接管理选项。 keep-alive开启长连接
  • Content-Length:指定请求体的长度。
  • Content-Type:指定请求体的类型。
  • Cookie:包含与服务器之间共享的Cookie信息。 set-cookie设置的那些字段
  • Host:指定服务器的域名和端口号。
  • If-Modified-Since:指定只有在指定日期之后修改过的资源才会返回。 后面说,浏览器缓存
  • If-None-Match:指定只有在ETag不匹配时才会返回资源。 后面说,浏览器缓存
  • Referer:指定发起请求的页面的URL。 跨站请求伪造服务端可以监测这个来判断
  • User-Agent:指定客户端的类型。

请求体

GET请求通常不包含请求体,但是不是不能包含,因为GET请求一般是获取服务器资源,POST请求通常包含请求体。

查询浏览器缓存

浏览器缓存,把这张图搞懂,这块就没问题了

image.png

下面来说一下之前的那三个字段:

强缓存

服务器在第一次返回资源的时候,会为资源设置一个过期时间,

HTTP1.1之前是设置expires(绝对过期时间),和cookie那里的策略很像,但是不是一个东西,不要弄混了。cookie的缓存策略这篇里面有。他会存在问题和cookie expires一样的问题。

Cookie,Session,Token,同源策略、跨域、XSS、CSRF、DNS劫持一次性搞明白 - 掘金 (juejin.cn)

HTTP1.1设置的是cache-control,有以下取值:max-age,no-cache,no-store,private,public

Cache-Control:max-age=3000 // 代表着资源的有效期是3000秒

no-cache:直接走协商缓存,后面说

no-store:不缓存,直接从服务器拉取资源

private:指定响应只能被单个用户缓存,不能被共享缓存(例如代理服务器)缓存

public:如果客户端通过代理服务器访问服务器,则代理服务器也可以缓存该响应

总结

客户端会首先通过强缓存判断资源有没有缓存过,有没有过期,如果资源没有过期,直接从缓存中读取,就不会发起请求了,这其实也是一种性能优化手段。

协商缓存

这部分跟本文主题无关,属于补充内容。

服务器会在第一次返回该资时携带以下字段(注意k-v关系),然后服务器也存了一份: Etag:资源的唯一标识

            Etag:资源的唯一标识 `
            Last-Modify: Thu,31 Dec 2037 23:59:59 GMT。

客户端通过expires和cache-control发现缓存过期了,不能直接拿,就会在请求头中带上下面这两个k-v:

            If-None-Match:资源的唯一标识 
            If-Modify-Since:Thu,31 Dec 2037 23:59:59 GMT。

注意上面两组K-V的关系!

If-None-Match优先级高,服务器收到之后,

先对If-None-Match:资源的唯一标识做判断(先记住,后面说为什么),通过与之前生成的对比,如果资源没变,返回304缓存重定向,变了,返回200资源。

然后就没If-Modify-Since:Thu,31 Dec 2037 23:59:59 GMT什么事了。但是如果不传If-None-Match字段,那么就校验If-Modify-Since字段。

为什么If-None-Match优先级高于If-Modify-Since?

1.因为If-None-Match带的Etag,Etag是资源的唯一标识,更准确。比如某些文件定期生成内容没变,If-Modify-Since变了,但是Etag没变。

2.因为If-None-Match精度高,If-Modify-Since精度是秒。文件一秒被修改多次,If-Modify-Since体现不出来。

缓存位置

针对浏览器缓存再做补充,逐渐偏题,不想看的同学可以跳过。

前提: 浏览器拿到资源缓存到哪儿,是浏览器自己决定的,服务器并没有一个响应字段来控制缓存位置。浏览器缓存有以下四个位置,具体将资源存储在硬盘缓存还是内存缓存中,取决于浏览器的缓存策略和用户的操作。而且优先级不同。如果用户在浏览网页时频繁切换页面,那么浏览器就会将这些页面中的资源存储在内存缓存中,以便快速响应用户的请求。下面我由高到低进行描述:

service worker

MDN原话:

浏览器与网络(可用时)之间的代理服务器。它会拦截网络请求并根据网络是否可用来采取适当的动作、更新来自服务器的的资源。Service worker 运行在 web worker 上下文:因此它无法访问 DOM(了解web worker这个线程的都知道,它主要是做一些计算量大的脏活累活,它不能操作DOM元素的)。

基于以上的特性,它很适合拿来做缓存,而且是离线缓存,通过判断网络状态来采取适当的动作。

memory (内存)

内存中存一些经常变动的资源,关闭了浏览器之后就消失了,而且它读取速度快,占用空间小。

disk(硬盘)

硬盘空间比内存大,读取慢,一般存一些不怎么变动的资源。

疑惑

为什么硬盘存不怎么变的,内存存变化的资源?

因为内存缓存能够快速读取数据,所以将经常变动的资源存储在内存中,能够快速响应用户的请求。而硬盘缓存能够存储更多的数据,所以将不经常变动的资源存储在硬盘中,能够减少对服务器的请求次数,提高网页的加载速度。

HTTP2 push

这个我在书上看到也是缓存的一个位置,但是我不觉得是,它就是服务端推送资源啊。跟缓存有什么关系?不是还是要客户端去请求吗,如果有大佬懂欢迎在评论区给小弟讲解一下。

DNS查询

为什么要有DNS:

浏览器缓存中没有的话,就要发起请求了,那么第一步就是去找到对应服务器的IP地址,为什么要有域名解析系统呢,因为我们访问服务器用的是域名,真正的设备用的IP地址,我们不可能记住所有服务器的IP地址(百度、bilbili、掘金...),所以需要这样一个系统把我们输入的域名转换成IP地址~

DNS流程

这个需要在脑子过一遍

image.png

TCP建立连接

传输层是应用层的基础,所以在发送HTTP请求之前,还要先建立TCP连接。

TCP三次握手流程相信大家都已经很清楚了。

为什么要三次握手?

原因1:

三次握手可以防止历史数据包初始化了连接。

两次握手为什么不行?

两次握手场景:

1.客户端发送数据包1,但是这个时候断网了。网络重连之后,客户端又发送了一个数据包2。

2.网络连接后,服务器先接收到数据包1并返回,服务器就处于establish状态,客户端收到数据包1的ACK包,发现并不是自己所期望的ACK包,于是RST。但是从服务器发送ACK到接收到RST这段时间内,白白浪费了服务器资源,服务器还可能发送数据包给客户端。

原因2:

保证都有发送和接收的能力(本质是双方需要同步序列号)

四次握手场景

1.客户端发送一个数据包,随机生成序列号seq1,服务器返回一个seq+1,这说明了什么?

                这说明客户端有发的能力,服务端有收的能力。(客户端发出去被收到了)            

2.服务器发送序列号seq2,客户端接收seq2+1,说明服务端有发的能力,客户端有收的能力。 3.同时双方还同步了初始序列号,便于后续的有序传输。

考虑一个问题

服务器返回seq+1和发送seq2两步是不是可以合成一步,那就三次就可以咯,两次不行。这里的思想就和第三次握手可以到数据一样,大家仔细体会体会。

好了,传输层连接建立好了那么下一步还要往网络层、物理层走,由于这里前端应该不需要太了解,所以就省略了,总之就是一个数据包最终会从物理网络发出去,经过路由转发,IP定位,最终找到目标服务器。服务器进行一层层的解析,把HTTP请求报文取出来。(这部分省略了大片篇幅!)

服务器生成HTTP响应报文

HTTP响应包括:

状态行、响应头、空行、响应体。

1.状态行:HTTP1.1 200 OK

2.HTTP响应头:

  • Content-Type:指示资源的MIME类型。
  • Date:消息发送的日期和时间。
  • Server:服务器软件名称和版本。
  • Set-Cookie:设置HTTP cookie。
  • Transfer-Encoding:传输编码方式。
  • Etag:协商缓存
  • Last-Modified:协商缓存
  • cache-control:强缓存
  • Expires:强缓存

HTTP状态码:

很多,需要记忆,这个也好查,就不讲了

服务器的响应报文把请求报文走过的路反着又走了一遍,然后到客户端浏览器,下面来讲浏览器的渲染过程。

浏览器渲染原理

JS是单线程的,但是浏览器是多进程多线程的。浏览器是如何渲染出页面的呢?

1.浏览器中的网络进程收到 HTML 文件传给浏览器进程中的 UI 线程
2.UI 线程接收到 HTML 文件通过 IPC 管道传递给浏览器渲染进程 
3.渲染进程主线程绘制 DOM 树 为每个 DOM 节点添加样式 然后用 DOM 树和 style 信息生成 layout tree 
4.生成绘制顺序表 paint(z-index) 
5.利用 layout tree 和 paint 生成 layer 图层 
6.渲染器主进程将 paint 和 layer tree 信息传给合成器线程 
7.合成器线程对图层进行分块 生成更小的图块 tiles 
8.合成器线程将 tiles 给栅格线程 栅格线程对每一个图块栅格化 生成 draw quads 图块信息 
9.合成器线程收集栅格线程的 draw quads 通过 IPC 管道传给浏览器进程 
10.浏览器进程将图块信息传回 GPU 进程 GPU 进程进行渲染

浏览器是如何解析DOM和CSSOM的

解析DOM树:每当解析遇到标签,token生成器会生成一个token,另一个进程在消耗这些token,并将它们转换为节点对象。最终生成一个文档对象模型(DOM)。表示节点的内容和属性以及节点之间的嵌套关系。

解析CSSOM:首先浏览器解析CSS是阻塞的。CSS是层叠样式表,层叠就是如何得到各个标签的所有样式。

(1)确定声明值:通过浏览器的默认样式表和作者写的样式表 找到作者写了的和浏览器没有冲突的样式 直接作为计算后的样式值 并且将一些预设值转换成绝对值 比如颜色=>rgb

(2)层叠:让已经冲突的样式进行厮杀.优先级作者important>浏览器important>作者写的(选择器优先级)>浏览器默认样式 这个优先级vscode直接显示

(3)继承:没有算出来的属性继承 跟文字相关的可以继承
盒模型 位置 宽高那些不能继承

(4)使用默认值 所有属性都必须有值的

(5)如果优先级一样,后面的会覆盖上面的。

JS为什么会阻塞页面的解析和渲染:因为JS可以操作DOM或者改变DOM的样式。

回流与重绘

  • 当改变页面元素的颜色时 会发生重绘 重新触发样式计算(dom)(style)和绘制(paint) 走下面的一系列流程 占用渲染器进程(浏览器内核)的主线程。

  • 当外面改变页面元素的位置布局时 会触发重排 重新把 dom-layer 都走 走下面一系列流程 占用渲染器进程的主线程。

区别:

            重排一定有重绘,重绘不一定有重排
image.png