BetaSu / fe-hunter

每天一道题,3个月后,你就是面试小能手,答题还能赚钱哦
1.67k stars 116 forks source link

浏览器缓存策略知多少? #64

Open BetaSu opened 2 years ago

BetaSu commented 2 years ago

发生问题的场景

浏览器的缓存策略一般分为强缓存协商缓存,你知道他们之间的区别吗?

需要解决的问题

请按照一下顺序回答:

1.什么是强缓存?什么是协商缓存? 2.与他们有关的请求/响应头字段有哪些?分别是什么含义? 3.从缓存角度回答 Ctrl + F5 强制刷新网页和 F5 刷新网页的区别。

最佳答案评选标准

  1. 答案遵循以上顺序作答

最佳答案

悬赏中,欢迎作答...

答题同学须知

围观同学须知

xianguoGou commented 2 years ago

强缓存:根据服务器响应头字段(Expires或Cache-Control)的过期时间,来判断是否需要发送HTTP请求或者读取缓存数据的一种策略; 协商缓存:当强缓存失效之后,浏览器在请求头中携带相应的缓存tag(Last-Modified 或 ETag)来向服务器发请求,由服务器根据这个tag,来决定是否使用缓存;

  1. Expires :即过期时间,存在于服务端返回的响应头中,告诉浏览器在这个过期时间之前可以直接从缓存里面获取数据,无需再次请求;

  2. Cache-Control:采用过期时长来控制缓存而非具体的过期时间,对应的字段有

    • max-age:过期时长。以秒为单位,例如 Cache-Control:max-age=3600,表示这个响应返回后在 3600 秒,也就是一个小时之内可以直接使用缓存
    • private:只有浏览器能缓存,中间的代理服务器不能缓存
    • no-cache: 跳过当前的强缓存,发送HTTP请求,即直接进入协商缓存阶段
    • no-store:不进行任何缓存
    • s-maxage:和 max-age差不多,但是区别在于s-maxage是针对代理服务器的缓存时间
    • must-revalidate:这个字段判断一旦缓存过期,就必须回到源服务器验证

在HTTP/1.0和HTTP/1.1当中,这个字段是不一样的。在早期,也就是HTTP/1.0时期,使用的是Expires,而HTTP/1.1使用的是Cache-Control

  1. Last-Modified: 即最后修改时间。在浏览器第一次给服务器发送请求后,服务器会在响应头中加上这个字段。

    • If-Modified-Since:浏览器接收到后,如果再次请求,会在请求头中携带If-Modified-Since字段;
  2. ETag: 服务器根据当前文件的内容,给文件生成的唯一标识,只要里面的内容有改动,这个值就会变。

    • If-None-Match:服务器通过响应头把这个值给浏览器。浏览器接收到ETag的值,会在下次请求时,将这个值作为If-None-Match这个字段的内容,并放到请求头中,然后发给服务器

F5 刷新网页会让浏览器跳过检查强缓存,直接去服务器检查协商缓存,如果命中则Status Code会显示304 Not Modified

Ctrl + F5 强制刷新网页会导致浏览器直接跳过强缓存跟协商缓存,直接去请求新的资源,返回状态码200 OK

LK-Champ commented 2 years ago

前端缓存

缓存(Brower Caching)是指在本地磁盘对用户最近请求过的文档进行存储,当访问者再次访问同一页面时,就可以直接从本地磁盘加载文档。在前端中,缓存可以可分为两个大类:

HTTP 缓存

HTTP 缓存按照失效策略可分为:

Cache-Control

HTTP/1.1定义的强缓存字段,可以组合使用多种指令(多个指令之间可以通过 “,” 分隔)

强缓存生成过程

  1. 浏览器->浏览器缓存:发生 http 请求,询问是否存在浏览器缓存。
  2. 浏览器缓存 ->浏览器 :没有请求资源的缓存数据。
  3. 浏览器 -> 服务器:发生请求,或者最新的资源数据。
  4. 服务器 -> 浏览器:返回资源和缓存标识。
  5. 浏览器 -> 浏览器缓存: 根据缓存标识,进行资源缓存。

强缓存生效过程

  1. 浏览器 -> 浏览器缓存:发生请求,最开始会查询浏览器缓存是否有浏览器缓存。
  2. 浏览器缓存 -> 浏览器:有缓存资源,并且没有失效,将缓存给到浏览器。

协商缓存

向服务器发送请求,服务器会根据这个请求的 request header 的一些参数来判断是否命中协商缓存,如果命中,则返回304状态码并带上新的response header通知浏览器从缓存中读取资源;

Last-Modified 与 If-Modified-Since

Last-Modified 资源的最后更新时间,当浏览器请求服务器资源,在响应头中设置 Last-Modified,浏览器会将拿到的 Last-Modified 存储起来。当浏览器再次访问该资源时会设置 If-Modified-Sine( = Last-Modified),放入请求头,发送到服务器。服务器会根据资源的更新情况进行判断是否需要返回新的资源,返回新的资源,状态码200,返回新的 Last-Modified。资源没有更新,返回状态码304,返回旧的 Last-Modified。

但是 Last-Modified 也有自己的弊端。它的最小但是是秒,试想一下,如果服务器资源的修改资源的速度非常的快,快到毫秒级别,那么服务器会误以为资源是没有被修改的,导致浏览器获取不到最新的资源。还有另外一种情况就是服务器资源是被修改过了,但是资源的内容没有改变,这种情况服务器会重新设置 Last-Modified,当浏览器请求资源携带 If-Modified-Sine,服务器进行If-Modified-Sine 和 Last-Modified 进行对比时,会发现是不同,从新加载资源。

Etag 与 If-None-Match

Etag 是服务器给资源打上的一个标记。当浏览器请求服务器资源,在响应头设置 Etag ,浏览器拿到这个 Etag,会将它存储起来,当再次请求访问该资源是,会在请求头上设置 If-None-Match (= Etag),放入请求头,发生到服务器,服务器会根据资源的更新情况进行判断是否需要返回新的资源,返回新的资源,状态码200,返回新的 Etag。资源没有更新,返回状态码304,返回旧的 Etag。

相对于 Last-Modified,Etag 到底由撒优势了?这需要从 Etag 的生成来说起。 Etag 的生成一般有两种情况:

缓存新鲜度

这里可以在随带说一下缓存新鲜度。 缓存新鲜度意为指明缓存是否新鲜,简单来说缓存是否在保质期,有么有过期。缓存是否新鲜取决于两个因素,缓存新鲜度(保质期多长)和缓存使用期(已经过去了多长时间)。

响应使用期 = max(age_value, max(response_time - date_value)) 传输延迟时间 = response_time - request_time 停留缓存时间 = now - response_time

缓存使用期:= 响应使用期 + 传输延迟时间 + 停留缓存时间 = max(age_value, max(response_time - date_value)) + now - request_time。

response_time:浏览器缓存收到响应的本地时间,客户端时间
date_value:响应首部的 date 值,服务器创建报文的时间
age_value:响应首部 age 值,源服务器在多久前创建了响应或者代理服务器存储的时长
request_time:浏览器缓存发起请求的本地时间,客户端时间r
now:客户端当前时间,客户端时间

因此一旦修改了电脑客户端本地时间为未来时间,缓存使用期的计算便会受到影响,主要是停留缓存时间会变大,从而导致缓存使用期超出缓存新鲜度范围(强缓存失效)。 这便是 max-age 仍然受到本地时间影响的原因所在。

但是这里就会有同学有一个疑问?缓存新鲜度:= max-age || (expires - date),如果没有 max-age(s-maxage) 和 expires 这两个关键的字段值时,强缓存的新鲜度如何计算?如果是这样的话,只能说计算不了,但是浏览器会触发启发式缓存。

启发式缓存 启发式缓存会有一套计算缓存新鲜度的公式。 缓存新鲜度 = max(0,(date - last-modified)) * 10%

当然如果 last-modified 也没有,那浏览器就会有任何的缓存,每一次请求都是请求最新的资源。

浏览器缓存

浏览器缓存按照缓存位置可分为:

按照存储类型可分为:

memory cache

对于 memory cache、disk cache 来说也遵循访问的获取顺序:

  1. 优先从内存中获取资源,如果资源存在,就直接从内存缓存中获取资源。
  2. 如果内存中没有存在,就从磁盘中获取资源,如果资源存在,就直接从磁盘中获取资源。
  3. 如果磁盘中没有获取到,就进行网络请求,把获取到的资源存入到内存和磁盘中。

并且针对不同的资源存储的方式也不太一样:

加载模式

F5 刷新网页

“正常重新加载”模式会优先读取缓存。

Ctrl + F5 强制刷新网页

所有资源都重新向服务器获取,这个没有问题,但是检查下请求报头我们会发现,使用硬性重新加载后所有资源的请求首部都被加上了 cache-control: no-cache 和 pragma: no-cache,两者的作用都表示告知(代理)服务器不直接使用缓存,要求向源服务器发起请求,而 pragma 则是为了兼容 HTTP/1.0。 因此硬性重新加载并没有清空缓存,而是禁用缓存,其效果类似于在开发者工具 Network 面板勾选了 Disable cache 选项。

1049906948 commented 2 years ago

1.什么是强缓存?什么是协商缓存?

答:

1.强缓存 (也叫强制缓存)

对于强缓存,控制它的字段分别是:Expires和Cache-Control,其中Cache-Control优先级比Expires高。当客户端发出一个请求到服务器,服务器希望你把资源缓存起来,于是在响应头中加入了这些内容

Cache-Control: max-age=3600 我希望你把这个资源缓存起来,缓存时间是3600秒(1小时)
Expires: Thu, 10 Nov 2020 08:45:11 GMT 到达指定时间过期
Date: Thu, 30 Apr 2020 12:39:56 GMT
Etag:W/"121-171ca289ebf",(后面协商缓存内容)这个资源的编号是W/"121-171ca289ebf"
Last-Modified:Thu, 30 Apr 2020 08:16:31 GMT,(后面协商缓存内容)这个资源的上一次修改时间

Cache-Control和 Expires分别是HTTP/1.1 和 HTTP/1.0的内容,为了兼容 HTTP/1.0 和 HTTP/1.1,实际项目中两个字段我们都会设置。 浏览器收到这个响应之后就会做下面的事情

浏览器把这次请求得到的响应体缓存到本地文件中 浏览器标记这次请求的请求方法和请求路径 浏览器标记这次缓存的时间是3600秒 浏览器记录服务器的响应时间是格林威治时间2020-04-30 12:39:56

这一次的记录非常重要,它为以后浏览器要不要去请求服务器提供了依据。 之后当客户端收准备再次请求同样的地址时,它突然想起了一件事:我需要的东西在不在缓存里呢? 此时,客户端会到缓存中去寻找是否有缓存的资源,如下

https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/461e51029b60422f80cb202242fa6d64~tplv-k3u1fbpfcp-zoom-in-crop-mark:3024:0:0:0.awebp

判断缓存是否有效就是通过把max-age + Date,得到一个过期时间,看看这个过期时间是否大于当前时间,如果是,则表示缓存还没有过期,仍然有效,如果不是,则表示缓存失效。

2.协商缓存

一旦发现缓存无效,它并不会简单的把缓存删除,而是抱着一丝希望,想问问服务器,我这个缓存还能继续使用吗? 于是,浏览器向服务器发出了一个带缓存的请求 所谓带缓存的请求,无非就是加入了以下的请求头:

If-Modified-Since: Thu, 30 Apr 2020 08:16:31 GMT  亲,你曾经告诉我,这个资源的上一次修改时间是格林威治时间2020-04-30 08:16:31,请问这个资源在这个时间之后有发生变动吗?
If-None-Match: W/"121-171ca289ebf"  亲,你曾经告诉我,这个资源的编号是W/"121-171ca289ebf,请问这个资源的编号发生变动了吗?

之所以要发两个信息,是为了兼容不同的服务器,因为有些服务器只认If-Modified-Since,有些服务器只认If-None-Match,有些服务器两个都认,但是一般来说 If-None-Match 的优先级高于 If-Modified-Since 此时可能会产生两个结果

缓存失效:那么非常简单,服务器再次给予一个正常的响应(响应码200 带响应体),同时可以附带上新的缓存指令,浏览器缓存新的内容 缓存有效:服务器返回304重定向,并且响应头带上新的缓存指令,浏览器作出相应缓存动作。

https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/fd3db09a1cc04f7999eb14b0a021eefc~tplv-k3u1fbpfcp-zoom-in-crop-mark:3024:0:0:0.awebp

总结

当浏览器再次访问一个已经访问过的资源时,它会这样做: 1.根据相关字段判断是否命中强缓存,如果命中,就直接使用缓存了。 2.如果没有命中强缓存,就发请求到服务器检查是否命中协商缓存。 3.如果命中协商缓存,服务器会返回 304 告诉浏览器使用本地缓存。 4.否则,返回最新的资源。

imondo commented 2 years ago

这是来自QQ邮箱的假期自动回复邮件。   您好,我最近正在休假中,无法亲自回复您的邮件。我将在假期结束后,尽快给您回复。