浏览器缓存机制

325 阅读6分钟

缓存位置

浏览器按照资源大小决定资源缓存的位置

  1. service Worker 浏览器独立线程进行缓存
  2. Memory Cache:内存缓存
  3. Disk Cache:硬盘缓存
  4. push 擦cache(http2中的推送缓存)

打开网页,输入地址,查找硬盘中是否有匹配,有则使用,没有就发送网络请求

普通刷新:tab没有关闭,可以从内存中读取,然后是硬盘

强制刷新:浏览器不适用缓存,发送的请求头带有(Cache-control:no-cache,为了兼容ie还带有Pragma:no-cache),服务器直接返回200

缓存方式

浏览器的缓存方式有强缓存和协商缓存两种,当浏览器判定缓存文件过期后,就会询问源服务器资源的有效性。

强缓存

定义:用户发送的请求,直接从服务器缓存中获取,不发送请求到服务器,不与服务器产生交互行为

强缓存是利用http的返回头中的Expires或者Cache-Control两个字段来控制的,用来表示资源的缓存时间。

1. Expires http请求的实体首部字段。

该字段是http1.0时的规范,它的值为一个绝对时间的GMT格式的时间字符串,比如Expires:Mon,18 Oct 2066 23:59:59 GMT。这个时间代表着这个资源的失效时间。 缓存服务器收到含有Expires首部字段的请求时,如果在有效时间之内,会将响应副本文件返回,如果在有效期之外,会向源服务器发送请求,请求最新的资源。如果源服务器不希望对资源进行缓存时,最好将Expires的值设为与首部字段Date相同的值。

这种方式有一个明显的缺点,由于失效时间是一个绝对时间,所以当服务器与客户端时间偏差较大时,就会导致缓存混乱。

2. Cache-Control:max-age:xxxx Cache-Control是http1.1时出现的header信息,主要是利用该字段的max-age值来进行判断,它是一个相对时间,例如Cache-Control:max-age=3600,单位是秒,代表着资源的有效期是3600秒。cache-control除了该字段外,还有下面几个比较常用的设置值:

  • no-cache:不使用本地缓存。需要使用缓存协商,先与服务器确认返回的响应是否被更改,如果之前的响应中存在ETag,那么请求的时候会与服务端验证,如果资源未被更改,则可以避免重新下载。
  • no-store:直接禁止游览器缓存数据,每次用户请求该资源,都会向服务器发送一个请求,每次都会下载完整的资源。
  • public:可以被所有的用户缓存,包括终端用户和CDN等中间代理服务器。
  • private:只能被终端用户的浏览器缓存,不允许CDN等中继缓存服务器对其缓存。
  • max-age:从当前请求开始,允许获取的响应被重用的最长时间(秒)。

强缓存的使用对象 css文件,js文件等,我们一般不会对html文件进行强缓存处理。 使用方式:

  1. 在路径后面加上时间戳或者版本号作为标识。 例如
<link href="css/index.css?version=1.1"/>
  1. hash值,在资源的名字后加上hash值,这个值只要文件发生更新就会重新生成。webpack可以进行配置。

强缓存http的状态码都是200,无论是否走缓存

协商缓存

在强缓存失效的情况下,才会触发浏览器的协商缓存机制

定义:用户发送的请求,发送到服务器后,由服务器判定是否从缓存中读取资源

协商缓存就是由服务器来确定缓存资源是否可用,所以客户端与服务器端要通过某种标识来进行通信,从而让服务器判断请求资源是否可以缓存访问,这主要涉及到下面两组header字段,这两组搭档都是成对出现的,即第一次请求的响应头带上某个字段(Last-Modified或者Etag),则后续请求则会带上对应的请求字段(If-Modified-Since或者If-None-Match),若响应头没有Last-Modified或者Etag字段,则请求头也不会有对应的字段。

Last-Modified`/If-Modified-Since http 1.0

浏览器第一次请求一个资源的时候,服务器返回的header中会加上Last-Modified,Last-Modified是一个时间标识该资源的最后修改时间,例如Last-Modify: Thu,31 Dec 2037 23:59:59 GMT。

当浏览器再次请求该资源时,request的请求头中会包含If-Modify-Since,该值为缓存之前返回的实体首部字段Last-Modified的值。服务器收到If-Modify-Since后,将该值与服务器保存的资源的最后修改时间对比,判断是否命中缓存。

如果命中缓存,则返回状态码为304的响应,并且不会返回资源内容,并且不会返回Last-Modify。 如果没有命中缓存,服务器会返回新的资源和Last-Modified,状态码为200,浏览器接收到响应后,后重新对资源和Last-Modified值进行保存,以便下次协商缓存的时候使用。

ETag/If-None-Match http 1.1

与Last-Modify/If-Modify-Since不同的是,Etag/If-None-Match返回的是一个校验码。ETag可以保证每一个资源是唯一的**,资源变化都会导致ETag变化**。服务器根据浏览器上送的If-None-Match值来判断是否命中缓存。

与Last-Modified不一样的是,当服务器返回304 Not Modified的响应时,由于ETag重新生成过,response header中还会把这个ETag返回,即使这个ETag跟之前的没有变化。

为什么要有Etag

你可能会觉得使用Last-Modified已经足以让浏览器知道本地的缓存副本是否足够新,为什么还需要Etag呢?HTTP1.1中Etag的出现主要是为了解决几个Last-Modified比较难解决的问题:

  • 一些文件也许会周期性的更改,但是他的内容并不改变(仅仅改变的修改时间),这个时候我们并不希望客户端认为这个文件被修改了,而重新GET;
  • 某些文件修改非常频繁,比如在秒以下的时间内进行修改,(比方说1s内修改了N次),If-Modified-Since能检查到的粒度是s级的,这种修改无法判断(或者说UNIX记录MTIME只能精确到秒);
  • 某些服务器不能精确的得到文件的最后修改时间。

Last-Modified与ETag是可以一起使用的,服务器会优先验证ETag,一致的情况下,才会继续比对Last-Modified,最后才决定是否返回304。

用户行为

img

用户刷新界面,永远都不会提取本地缓存的文件