笔者在准备面试过程中遇到这个问题,现在将这个问题的知识点进行整合分析,归纳完全知识点。
- 用户在浏览器中输入url,浏览器接收到url。
2.浏览器接收到这个url之后,会根据这个url会先查看缓存,如果有缓存且没有过期的话直接提供给客户端,完成页面渲染。 这个过程涉及到强缓存和协商缓存。
1、强缓存(本地缓存)
1.1. 概念
不用跟服务器进行通信,直接使用本地缓存的资源,
1.2. 相关header字段
- expires ------
HTTP1.0使用的expires
一个未来时间,代表资源的有效期,没有过期之前都使用当前资源。 - cache-control------
HTTP1.1及其之后使用cache-control
max-age: 当值设为max-age=300时(即300s),则代表在这个请求正确返回时间(浏览器也会记录下来)的5分钟内再次加载资源,就会命中强缓存。
no-cache:不使用本地缓存。向浏览器发送新鲜度校验请求
no-store:禁止浏览器缓存数据,也禁止保存至临时文件中,每次都重新请求,
pubilc:任何情况下都缓存(即使是HTTP认证的资源)
private:只能被终端用户的浏览器缓存,不允许CDN等中间层缓存服务器对其进行缓存。
2、协商缓存
2.1、概念
首先,将所缓存资源的信息发送给服务器,
其次,让服务器判断资源是否已经更新了,
- 若已更新,则返回更新后的资源;
- 若没有更新,则返回304状态,告诉浏览器可直接使用本地缓存的资源,
整个过程至少与服务器通信一次
2.2、相关header字段
响应头: Last-Modified
请求头: If-Modified-Since( 资源修改的时间 )
-
浏览器第一次发请求,服务器在返回的 respone 的 header 加上 Last-Modified, 表示资源的最后修改时间
-
再次请求资源,在 requset 的 header 加上 If-Modified-Since , 值就是上一次请求返回的 Last-Modified 值
-
服务器根据请求传过来的值判断资源是否有变化,没有则返回 304, 有变化就正常返回资源内容,更新 Last-Modified 的值
-
304 从缓存加载资源,否则直接从服务器加载资源
-
响应头:Etag
请求头:If-None-Match(标识符字符串)一个标识符字符串,表示文件唯一标识,只要文件内容改动,ETag就会重新计算。缓存流程和 Last-Modified 一样。
2.3、Last-Modified 与 Etag 的对比:
- 如果我们打开文件但并没有修改其内容,Last-Modified 也会改变,而Etag则不会改变。
- Last-Modified 的时间单位为秒,如果一秒内对文件进行了多次修改,那么由于其时间单位是秒,所以Last-Modified不会改变,最终浏览器还是会去读取缓存资源,而此时缓存的资源已经过时了。
- Etag的优先级高于Last-Modified。
2.4、为什么协商缓存有两个属性一起出现
协商缓存是浏览器判断资源是否可用,所以需要两个标识,
第一个是第一次请求的响应头带上某个字段(Last-Modified或者Etag),
第二个则是后续请求带上的对应请求字段(If-Modified-Since或者If-None-Match),
两个标识一起出现才有意义,单独则没有什么效果。
3、浏览器缓存过程(强缓存转协商缓存的过程)
- 1.浏览器第一次加载资源,服务器返回200,浏览器将资源文件从服务器上请求下载下来,并
把文件、文件的返回时间、response header一并缓存; - 2.下一次加载资源时,查看
cache-control的设置 -
- 如果值是
no-cache,则表示不缓存,则直接去请求数据;
- 如果值是
-
- 如果值是
max-age,则比较当前时间和上一次返回200时的时间差是否大于max-age,若小于max-age,则表示没有过期,命中强缓存,不发请求直接从本地缓存读取该文件;如果时间差大于max-age,则表示资源过期了,则向服务器发送请求,并且header中携带有If-None-Match、If-Modified-Since、Etag值等
- 如果值是
-
- 3.如果服务器收到的请求头中,有Etag值,
优先根据Etag的值判断被请求的文件有没有做修改,
如果Etag值一致则没有修改,命中协商缓存,返回304,则读取缓存资源;
如果Etag值不一致则有改动,则直接返回新的资源,并带上新的Etag值;
- 3.如果服务器收到的请求头中,有Etag值,
- 4.如果服务器收到的请求头中,没有Etag值,则将
If-Modified-Since和被请求文件的最后修改时间做比对,
如果一致则没有修改,命中协商缓存,返回304;
如果不一致则有修改,则返回新的文件和并在响应头中携带last-modified,下次请求时通过If-Modified-Since携带上last-modified的值;
3.否则浏览器就会通过DNS解析url ,获得协议名、主机名、端口号。
DNS解析详解:
-
输入域名时,操作系统会先检查自己本地host文件中是否有这个网址的映射关系,如果有,就调用这个IP地址映射,完成域名解析。
-
如果host没有这个域名的映射,则查找本地的DNS解析器缓存,是否有这个网址映射关系,如果有,直接返回,完成域名解析。
-
如果本地解析器缓存没有的话,则查找本地DNS服务器,如果要查询的域名包含在本地配置资源中,则完成域名解析。
-
如果本地DNS服务器没有的话,就会请求根服务器,根服务器就会返回一个负责该区域的主服务器IP
-
本地域名服务器根据主服务器IP,链接到这个主域名服务器,如果有这个域名的话,就调用这个IP地址映射,完成域名解析。如果没有的话,则递归请求下一级域名服务器,直到找到对应的域名
4.然后浏览器就会根据这个IP跟对应的服务端建立tcp连接 TCP三次握手:
第一次握手:客户端向服务器发送一个 SYN=1 报文,并指定客户端的初始化序列号 seq=x。此时客户端处于SYN_Sent 状态
第二次握手:服务器接收到客户端的 SYN=1 报文后,同意连接的话,会发出一段确定报文。确定报文中应该ACK= 1,SYN= 1,确认号是ack=x+1,同时也要为自己初始化一个序列号seq=y.此时服务器处于 SYN-RCVD
第三次握手:客户端接收到 SYN 报文之后,还会向服务器发送确定。确定报文的 ACK=1,ack=y+1 ,自己的序列号变成 x+1。TCP 连接建立,客户端处于 已连接状态 。
5.当服务器接受到客户端的确认后也进入了 已连接状态 。此时双方就可以开始通信了
6.接下来就是浏览器向服务器发送HTTP请求 TCP 连接建立之后,浏览器端会构建请求行、 请求头等信息,并把和该域名相关的 Cookie 等数据附加到请求头中,然后向服务器发送构建的请求信息。如果是 HTTPS,还需要进行 TSL 协商。服务器还会检查 HTTP 的请求头,看是否包含缓存信息。
7.服务器接受并解析这个请求然后发送一个数据包给浏览器
8.关闭浏览器与服务器之间的连接 TCP四次挥手:
-
第一次挥手:客户端向服务端发送一个FIN报文,报文中指定一个序列号。此时,客户端处于 FIN_WAIT-1状态
-
第二次挥手:服务端收到客户端 发送的 FIN报文后,会将客户端的序列号+1 作为ACK报文的序列号值发送给客户端,表明收到了客户端的报文。然后服务器就会处于 CLOSE-WAIT-1状态
-
第三次挥手:服务端向客户端发送 FIN报文,且指定一个序列号。此时,服务端处于 LAST_ACK状态
-
第四次挥手:客户端收到服务端发送的FIN报文后,会将服务端的序列号+1 作为ACK报文的序列号值发送给服务端,此时客户端处于 TIME_WAIT 状态。需要等服务端确定收到自己的ACK报文后才会进入 CLOSED 状态 服务端接收到 ACK报文后,就关闭连接,处于 CLOSED 状态。 9.浏览器就会根据这个数据包解析HTML文档,构建DOM树,构建CSSOM树,解析js脚本,下载资源 渲染的具体过程:
-
浏览器会通过 HTML Parser (HTML解析器)根据深度遍历的原则,将html解析成 DOM tree (DOM 树)。
-
浏览器会通过 CSS Parser (CSS解析器)将CSS 解析成 CSS Rule Tree(CSSOM 树)。
-
浏览器会将 javascript 通过 DOM API 或者 CSSOM API 将 JS代码进行解析并应用到布局中,且会呈现出响应式的结果。
-
根据 DOM 树 与 CSSOM 树构建出响应的 render Tree。
-
接下来就是进行重排(reflow)与重绘(repaint)。当页面中任意一个节点的几何尺寸发生变化的时候,就会触发重排,就会重新计算页面中所有的节点的位置。当页面中任意元素的样式属性发生变化时(几何尺寸不发生变化),就会发生重绘,重新绘画发生变化的元素。重排一定会触发重绘,而重绘不一定会有重排。
-
paint:绘制。遍历render Tree,并调用硬件图形API 来绘制每个节点到页面上。
10.最终就会显示出这个页面
总的来说分为以下几步