1. 首先,什么是浏览器缓存?
浏览器缓存(Brower Caching)是浏览器对之前请求过的文件进行缓存,以便下一次访问时重复使用,节省宽带,提高访问速度,降低服务器压力。
http缓存机制主要在http响应头部中设定,响应头中相应字段为Expires、Cache-Control、Last-Modified、Etag。
缓存的数据,必须在第一次请求获取到资源后,然后根据返回的信息来告诉如何缓存资源,可能采用的时强制缓存,也可能告诉客户端浏览器时协商缓存,这都需要根据响应的header内容来决定的。下面用两幅图来描述浏览器的缓存是怎么回事:
浏览器第一次请求时:
浏览器后续再进行请求时:
从上图可以知道,浏览器缓存包含两种类型,即强缓存(本地缓存)和协商缓存,浏览器在第一次请求发生后,再次请求时:
1. 浏览器在请求某一资源时,会优先获取该资源缓存的header信息,判断是否命中强制缓存(cache-control和expires信息),若命中直接从缓存中获取资源信息,包括缓存header信息;本次请求就避免了和服务器进行通信;
2. 如果灭有命中强制缓存,浏览器会发出请求到服务器,请求会携带第一次请求返回的有关缓存的header字段信息(Last-Modified/If-Modified-Since和If-None-Match),由服务器根据请求中的相关header信息来对比结果是否协商缓存命中;若命中,则服务器返回新的响应header信息更新缓存中的对应header信息,但是并不返回资源内容,它会告知浏览器可以直接从缓存中获取;否则返回最新的资源内容;
强制缓存和协商缓存的区别可以用下表来进行描述:
获取资源形式 | 状态码 | 发送请求到服务器 | |
强缓存 | 从缓存取 | 200(from cache) | 否,直接从缓存取 |
协商缓存 | 从缓存取 | 304(not modified) | 是,正如其名,通过服务器来告知缓存是否可用 |
2. 强制缓存相关的header字段
强制缓存上面已经介绍了,直接从缓存中获取资源而不经过服务器;与强制缓存相关的header字段又两个:
1. expires, 这是http 1.0时的规范;它的值为一个绝对时间的GMT格式的时间字符串,如 Fri, 05 Jul 2019 10:36:26 GMT, 如过发送请求的时间在expires之前,那么本地缓存始终有效,否则就会发送请求到服务器来获取资源
2. cache-control: max-age=number, 这是http 1.1 时出现的header信息,主要时利用该字段的max-age值来进行判断,它时一个相对的值;资源第一次的请求时间和Cache-Control设定的有效期,计算出一个资源过期时间,再拿这个过期时间和当前的请求时间比较,如果请求时间在过期时间之前,就能直接命中缓存,否则就不行;cache-control除了该字段外,还有下面几个比较常用的设置值:
注意:如果cache-control与expires同时存在的话,cache-control的优先级高于expires
3. 协商缓存相关的header字段
协商缓存都是由服务器来确定缓存资源是否可用的,所以客户端与服务器端要通过某种标识来进行通信,从而让服务器判断请求资源是否可以缓存访问,这主要涉及到下面两组header字段,这两组搭档都是成对出现的,即第一次请求的响应头带上某个字段(Last-Modified或者Etag),则后续请求则会带上对应的请求字段(If-Modified-Since或者If-None-Match),若响应头没有Last-Modified或者Etag字段,则请求头也不会有对应的字段。
4. Last-Modified和Etag区别
你可能会觉得使用Last-Modified已经足以让浏览器知道本地的缓存副本是否足够新,为什么还需要Etag呢?HTTP1.1中Etag的出现主要是为了解决几个Last-Modified比较难解决的问题:
一些文件也许会周期性的更改,但是他的内容并不改变(仅仅改变的修改时间),这个时候我们并不希望客户端认为这个文件被修改了,而重新GET;
某些文件修改非常频繁,比如在秒以下的时间内进行修改,(比方说1s内修改了N次),If-Modified-Since能检查到的粒度是s级的,这种修改无法判断(或者说UNIX记录MTIME只能精确到秒);
某些服务器不能精确的得到文件的最后修改时间。
这时,利用Etag能够更加准确的控制缓存,因为Etag是服务器自动生成或者由开发者生成的对应资源在服务器端的唯一标识符。
Last-Modified与ETag是可以一起使用的,服务器会优先验证ETag,一致的情况下,才会继续比对Last-Modified,最后才决定是否返回304。
5. 用户操作对缓存的影响
6. 强制缓存如何重新加载缓存过的资源
上面说到,使用强缓存时,浏览器不会发送请求到服务端,根据设置的缓存时间浏览器一直从缓存中获取资源,在这期间若资源产生了变化,浏览器就在缓存期内就一直得不到最新的资源,那么如何防止这种事情发生呢?
通过更新页面中引用的资源路径,让浏览器主动放弃缓存,加载新资源。
类似下图所示:
这样每次文件改变后就会生成新的query值,这样query值不同,也就是页面引用的资源路径不同了,之前缓存过的资源就被浏览器忽略了,因为资源请求的路径变了。