请求的流程
对于一次已经有缓存存在的请求来说(即之前已经发过针对这个资源的请求,在本地已经有缓存),如果发起请求,那么
首先会去找到缓存资源的响应头中的expires(过期时间)和cache-control(控制缓存的失效性)来判断当前是否直接使用缓存,如果当前时间还在expires之前,即缓存仍未失效的情况下,我们就直接使用缓存,这就是强缓存。
如果缓存已经失效,那么此时我们需要向后台发送请求,此时发送的请求并非直接就会从服务器获取资源内容,而是在请求头中,加入IF-Modified-Since(在其值后,资源是否更新)或者 IF-None-Match(比较Etag的值,相同则返回304,客户端从缓存中读取内容,否则返回200资源),这两个字段对应的值分别为第一次请求时返回的Last-Modified(上一次修改的时间)和Etag(资源的唯一标识),如果判断后资源尚未更新,就继续访问缓存资源,不会返回新的资源内容,如果已经更新,则会返回资源的实际内容,并更新header中相关的缓存字段,这就是协商缓存。
强缓存
强缓存是根据返回头中的Expires或者Cache-Control两个字段来控制的,都是表示资源的缓存有效时间。
Expires是 http 1.0 的规范,值是一个GMT 格式的时间点字符串,比如 Expires:Mon,18 Oct 2066 23:59:59 GMT 。这个时间点代表资源失效的时间,如果当前的时间戳在这个时间之前,则判定命中缓存。有一个缺点是,失效时间是一个绝对时间,如果服务器时间与客户端时间偏差较大时,就会导致缓存混乱。而服务器的时间跟用户的实际时间是不一样是很正常的,所以 Expires 在实际使用中会带来一些麻烦。
Cache-Control这个字段是 http 1.1 的规范,一般常用该字段的 max-age 值来进行判断,它是一个相对时间,比如
.Cache-Control:max-age=3600 代表资源的有效期是 3600 秒。并且返回头中的 Date 表示消息发送的时间,表示当前资源在 Date ~ Date +3600s 这段时间里都是有效的。不过我在实际使用中常常遇到设置了 max-age 之后,在 max-age 时间内重新访问资源却会返回 304 not modified ,这是由于服务器的时间与本地的时间不同造成的。当然 Cache-Control 还有其他几个值可以设置, 不过相对来说都很少用了:
no-cache 不使用本地缓存。需要使用协商缓存。
no-store直接禁止浏览器缓存数据,每次请求资源都会向服务器要完整的资源, 类似于 network 中的 disabled cache。
public 可以被所有用户缓存,包括终端用户和 cdn 等中间件代理服务器。
private 只能被终端用户的浏览器缓存。
如果 Cache-Control与 Expires 同时存在的话, Cache-Control 的优先级高于 Expires。
协商缓存
简单来说,协商缓存会通过在请求中添加If-Modified-Since字段和If-None-Match字段来向服务器确认当前资源是否继续使用缓存。
协商缓存是由服务器来确定缓存资源是否可用。 主要涉及到两对属性字段,都是成对出现的,即第一次请求的响应头带上某个字, Last-Modified 或者 Etag,则后续请求则会带上对应的请求字段 If-Modified-Since或者 If-None-Match,若响应头没有 Last-Modified 或者 Etag 字段,则请求头也不会有对应的字段。
Last-Modified/If-Modified-Since 二者的值都是GMT格式的时间字符串, Last-Modified 标记最后文件修改时间, 下一次请求时,请求头中会带上 If-Modified-Since 值就是 Last-Modified 告诉服务器我本地缓存的文件最后修改的时间,在服务器上根据文件的最后修改时间判断资源是否有变化, 如果文件没有变更则返回 304 Not Modified ,请求不会返回资源内容,浏览器直接使用本地缓存。当服务器返回 304 Not Modified 的响应时,response header 中不会再添加的 Last-Modified 去试图更新本地缓存的 Last-Modified, 因为既然资源没有变化,那么 Last-Modified 也就不会改变;如果资源有变化,就正常返回返回资源内容,新的 Last-Modified 会在 response header 返回,并在下次请求之前更新本地缓存的 Last-Modified,下次请求时,If-Modified-Since会启用更新后的 Last-Modified。
Etag/If-None-Match,值都是由服务器为每一个资源生成的唯一标识串,只要资源有变化就这个值就会改变。服务器根据文件本身算出一个哈希值并通过 ETag字段返回给浏览器,接收到 If-None-Match 字段以后,服务器通过比较两者是否一致来判定文件内容是否被改变。与 Last-Modified 不一样的是,当服务器返回 304 Not Modified 的响应时,由于在服务器上ETag 重新计算过,response header中还会把这个 ETag 返回,即使这个 ETag 跟之前的没有变化。
区别
对于强缓存来说,会直接去查看缓存资源中的响应头的字段值,以此来判断缓存的资源是否还能使用,在这个过程中,不需要向服务器发起请求。
而对于协商缓存来说,要判断缓存是否能使用,需要通过发起请求,带着与缓存相关的字段,到服务器去做过期判断后,才能通过相应的内容做出相应的操作。(是回去拿缓存的资源,还是拿这次服务器返回的资源)
所以说最终使用的是什么缓存,是由上一次请求得到的缓存资源中的头部来决定的,而这个资源是由服务器发来的,实际上就是由服务器来决定的。