slogeor/knife

浏览器缓存

slogeor opened this issue · 0 comments

浏览器缓存可以分为强缓存和协商缓存。

强缓存

原理

当浏览器对某个资源的请求命中强缓存时,返回的 HTTP 状态是 200,在 chrome 的开发者工具 network 里面 size 会显示为 from memory cache 和 from disk cache。

强缓存是利用 Expires 或者 Cache-control 这两个 HTTP response Header 实现的。它们都用来表示资源在客户端缓存的有效期。

cache

浏览器默认的缓存是放在内存内的,但我们知道,内存里的缓存会因为进程的结束或者说浏览器的关闭而被清除,而存在硬盘里的缓存才能够被长期保留下去。很多时候,我们在 network 面板中各请求的 size 项里,会看到两种不同的状态:from memory cache 和 from disk cache,前者指缓存来自内存,后者指缓存来自硬盘。而控制缓存存放位置的,不是别人,就是我们在服务器上设置的 Etag 字段。在浏览器接收到服务器响应后,会检测响应头部(Header),如果有 Etag 字段,那么浏览器就会将本次缓存写入硬盘中。

配置

  • 通过代码的方式,在 Web 服务器返回的响应头中添加 Expires 和 Cache-Control。

场景

针对静态资源可以使用强缓存,动态资源慎用。

协商缓存

原理

浏览器对某个资源的请求没有命中强缓存,就会发一个请求到服务器,服务器会验证协商缓存是否命中,如果协商缓存命中,请求响应返回的 HTTP 状态是 304,response 内容为空。

配置

协商缓存是利用 [Last-Modeified、If-Modified-Since][ETag、If-None-Match] 来实现的。

Last-Modeified && If-Modified-Since

执行过程

  • 1.浏览器第一次向服务器请求一个资源时,服务器在返回这个资源的同时,会在 response 的 Header 加上 Last-Modified 的 Header,这个 Header 表示这个资源在服务器上的最后修改时间
  • 2.浏览器再次向服务器请求这个资源时,在 request 的 Header 上加上 If-Modified-Since 的 Header,这个 Header 的值就是上一次请求时返回的 Last-Modified 的值
  • 3.服务器再次收到资源请求时,根据浏览器传过来 If-Modified-Since 和资源在服务器上的最后修改时间进行比对。
    • 没有变化则返回 304 Not Modified,但是不会返回此资源
    • 发生变化,就正常返回资源内容
  • 4.浏览器收到 304 的响应后,就会从缓存中加载资源,response Header 中不会再添加 Last-Modified 的 Header,因为既然资源没有变化,那么 Last-Modified 也就不会改变
  • 5.如果协商缓存没有命中,浏览器直接从服务器加载资源时,Last-Modified Header 在重新加载的时候会被更新,下次请求时,If-Modified-Since 会启用上次返回的 Last-Modified 值

存在问题

[Last-Modified,If-Modified-Since] 是根据服务器的时间返回的 Header,一般来说,在没有调整服务器时间和篡改客户端缓存的情况下,这两个 Header 配合起来管理协商缓存是非常可靠的,但是有时候服务器上资源其实有变化,但是最后修改时间却没有变化的情况,而这种问题又很不容易被定位出来,而当这种情况出现的时候,就会影响协商缓存的可靠性。

ETag && If-None-Match

执行过程

  • 1.浏览器第一次向服务器请求一个资源时,服务器在返回这个资源的同时,在 response 的 Header 加上 ETag,这个 Header 是服务器根据当前请求的资源生成的一个唯一标识,这个唯一标识是一个字符串,只要资源有变化这个串就不同,跟最后修改时间没有关系,所以能很好的补充 Last-Modified 的问题
  • 2.浏览器再次向服务器请求这个资源时,在 request 的 Header 上加上 If-None-Match,这个 Header 的值就是上一次请求时返回的 ETag 的值
  • 3.服务器再次收到资源请求时,根据浏览器传过来 If-None-Match 和根据资源内容生成一个新的 ETag 进行对比。
    • 没有变化则返回 304 Not Modified,但是不会返回此资源
    • 发生变化,就正常返回资源内容
  • 4.浏览器收到 304 的响应后,就会从缓存中加载资源
  • 5.如果有变化,就正常返回资源内容。
  • 6.与 Last-Modified 不一样的是,当服务器返回 304 Not Modified 的响应时,由于 ETag 重新生成过,response Header 中还会把这个 ETag 返回,即使这个 ETag 跟之前的没有变化

总结

强缓存不发请求到服务器,所以有时候资源更新了浏览器还不知道,但是协商缓存会发请求到服务器,所以资源是否更新,服务器肯定知道。

如果不启用强缓存的话,协商缓存根本没有意义。

对比

  • 浏览器在加载资源时,先根据这个资源的 HTTP Header 判断它是否命中强缓存,强缓存如果命中,浏览器直接从自己的缓存中读取资源,不会发请求到服务器。比如某个 CSS 文件,如果浏览器在加载它所在的网页时,这个 CSS 文件的缓存配置命中了强缓存,浏览器就直接从缓存中加载这个 CSS,连请求都不会发送到网页所在服务器
  • 当强缓存没有命中的时候,浏览器一定会发送一个请求到服务器,通过服务器端依据资源的 HTTP Header 验证这个资源是否命中协商缓存,如果协商缓存命中,服务器会将这个请求返回,但是不会返回这个资源的内容,而是告诉客户端可以直接从缓存中加载这个资源,于是浏览器就又会从自己的缓存中去加载这个资源
  • 强缓存与协商缓存的共同点是:如果命中,都是从客户端缓存中加载资源,而不是从服务器加载资源数据;区别是:强缓存不发请求到服务器,协商缓存会发请求到服务器
  • 当协商缓存也没有命中的时候,浏览器直接从服务器加载资源数据

浏览器缓存

浏览器缓存是将文件保存在客户端,在同一个会话过程中会检查缓存的副本是否足够新,在后退网页时,访问过的资源可以从浏览器缓存中拿出使用。通过减少服务器处理请求的数量,用户将获得更快的体验。

Expires

HTTP 1.0 提出的缓存过期时间,用来指定资源到期的时间,是服务器端的具体的时间点。

是绝对时间,Expires = max-age + 请求时间,需要和 Last-modified 结合使用。

Expires 是较老的强缓存管理 Header,由于它是服务器返回的一个绝对时间,在服务器时间与客户端时间相差较大时,缓存管理容易出现问题,比如随意修改下客户端时间,就能影响缓存命中的结果。

Cache-Control

HTTP 1.1 的时候,提出了一个新的 Header,就是 Cache-Control ,这是一个相对时间,在配置缓存的时候,以秒为单位,用数值表示。

Cache-Control 的优先级比 Expires 高。

Cache-Control 描述的是一个相对时间,在进行缓存命中的时候,都是利用客户端时间进行判断,所以相比较 Expires,Cache-Control 的缓存管理更有效,安全一些。

Cache-Control 属性

max-age

max-age(单位为s)设置缓存最大的有效时间,定义的是时间长短

比如 slogeor.com 上的 CSS 资源,max-age=2592000,也就是说缓存有效期为 2592000 秒(也就是30天)。于是在 30 天内都会使用这个版本的资源,即使服务器上的资源发生了变化,浏览器也不会得到通知。max-age 会覆盖掉 Expires。

s-maxage

s-maxage(单位为s)同 max-age,只用于共享缓存(比如CDN缓存)

比如,当 s-maxage=60 时,在这 60 秒中,即使更新了 CDN 的内容,浏览器也不会进行请求。也就是说 max-age 用于普通缓存,而 s-maxage 用于代理缓存。如果存在 s-maxage,则会覆盖掉 max-age 和 Expires Header。

no-cache

no-cache 指定不缓存响应,表明资源不进行缓存

设置了 no-cache 之后并不代表浏览器不缓存,而是在使用缓存前要向服务器确认资源是否被更改。因此有的时候只设置 no-cache 防止缓存还是不够保险,还可以加上 private 指令,将过期时间设为过去的时间

no-store

no-store 绝对禁止缓存,一看就知道如果用了这个命令当然就是不会进行缓存,每次请求资源都要从服务器重新获取。

public

指定响应会被缓存,并且在多用户间共享,默认为 public。

private

响应只作为私有的缓存,不能在用户间共享。如果要求 HTTP 认证,响应会自动设置为 private。

Last-modified

服务器端文件的最后修改时间,需要和 cache-control 共同使用,是检查服务器端资源是否更新的一种方式。当浏览器再次进行请求时,会向服务器传送 If-Modified-Since 报头,询问 Last-Modified 时间点之后资源是否被修改过。如果没有修改,则返回码为 304,使用缓存;如果修改过,则再次去服务器请求资源,返回码和首次请求相同为 200,资源为服务器最新资源。

ETag

根据实体内容生成一段 hash 字符串,标识资源的状态,由服务端产生。浏览器会将这串字符串传回服务器,验证资源是否已经修改。

浏览器刷新行为

刷新

点击刷新按钮或者按 F5,会触发这种行为。浏览器直接对本地的缓存文件过期,但是会带上If-Modifed-Since,If-None-Match(如果上一次 Response 带 Last-Modified, Etag)这就意味着服务器会对文件检查新鲜度,返回结果可能是 304,也有可能是 200

强制刷新

用户按 Ctrl+F5。浏览器不仅会对本地文件过期,而且不会带上 If-Modifed-Since,If-None-Match,相当于之前从来没有请求过,返回结果是 200

地址栏回车

浏览器发起请求,按照正常流程,本地检查是否过期,然后服务器检查新鲜度,最后返回内容

点击

和回车的效果是一样的

总结

用户主动触发的页面刷新行为(比如刷新按钮、右键刷新、F5 等),会导致浏览器放弃本地缓存,使用协商缓存( 304 缓存)

参考链接