浏览器的缓存策略是依靠 HTTP Header 来实现的,共分为两种:web
强缓存是指在缓存期间,请求不会发送到服务器,浏览器直接返回缓存结果,须要设置 Header:算法
expires: Wed, 10 Oct 2020 09:51:00 GMT
expires 是 HTTP/1.0 中用于控制网页缓存的字段,其值表明服务器返回该请求结果的缓存到期时间,也就是说,再次发起一样的请求时,若是客户端时间小于 Expires 的值,浏览器直接返回缓存结果。chrome
因为 expires 是采用客户端时间去和缓存失效时间作对比,但客户端时间是能够作修改的,若是客户端时间和服务端时间并不一样步,就会致使强缓存失效,或者时效变少。浏览器
因此,在 HTTP/1.1 中增长了 cache-control 头。缓存
cache-control 常见值为:服务器
咱们来看个例子:网络
这个例子中,expires 和 cache-control 都被设置了,可是 cache-control 优先级高,因此该资源会在 2592000 秒(也就是 30 天)后失效。学习
咱们能够得出 2 个结论:this
当咱们 F12 查看浏览器网络请求的时候,确定看到过这样的信息,from memory cache(内存缓存)和 from disk cache(磁盘缓存)。spa
当请求命中强缓存时,浏览器就会从内存或者磁盘中将缓存的资源返回来,请求不会到达服务器。
那么,哪些资源缓存在 memory,哪些缓存在 disk 呢?
关于 memory cache 和 disk cache,Chrome 官方有这么一段描述:
Caching
Chrome employs two caches — an on-disk cache and a very fast in-memory cache. The lifetime of an in-memory cache is attached to the lifetime of a render process, which roughly corresponds to a tab. Requests that are answered from the in-memory cache are invisible to the web request API. If a request handler changes its behavior (for example, the behavior according to which requests are blocked), a simple page refresh might not respect this changed behavior. To make sure the behavior change goes through, callhandlerBehaviorChanged()
to flush the in-memory cache. But don't do it often; flushing the cache is a very expensive operation. You don't need to callhandlerBehaviorChanged()
after registering or unregistering an event listener.
以上引用自 Chrome API
读取 memory 中的缓存资源,确定要比读取 disk 中的更快,可是 memory 中的缓存,会随着进程的释放而释放,也就是说,一旦咱们关闭 Tab 标签,memory 中的缓存也就没有了。
那么哪些资源会被缓存到 memory,哪些会缓存到 disk 中呢?关于这点我也没有找到定论,大多数的观点以下,供你们参考:
若是请求没有命中强缓存,或者强缓存失效后,就须要向服务器发起请求,验证资源是否有更新,这个过程叫作协商缓存。
当浏览器发起请求验证资源时,若是资源没有改变,那么服务器返回 304 状态码,而且更新浏览器缓存有效期;若是资源发生改变,那么服务器返回 200 状态码,而且返回相应资源,更新浏览器缓存有效期。
那么服务器如何肯定资源有没有更新呢,这里就要用到如下 2 组 HTTP 头。
last-modified 表示文件的最后修改日期,由服务器添加到 Response Header 中;if-modified-since 由浏览器添加到 Request Header 中,是上一次该资源的 last-modified 值。
服务器收到请求后,会将 if-modified-since 和服务器上该文件的修改时间戳进行比对,若是超过了缓存时间,那么则返回最新的资源,200 状态码,若是还在缓存有效期内,则返回 304 状态码。
上面这个例子能够看到:
Fri, 20 Dec 2019 12:44:01 GMT
Fri, 20 Dec 2019 12:44:01 GMT
这里服务器将 if-modified-since 的时间和服务器上文件的修改时间作比对,发现仍在缓存时间有效期内,因此直接返回 304 状态码,并不返回文件资源,由浏览器提供缓存好的资源。
可是 last-modified 也有它的缺点:
由于以上这些问题,因而在 HTTP/1.1 出现了 etag / if-none-match。
etag 相似于文件指纹,能够对文件内容作摘要算法,好比 md5,生成的值做为 etag 的值,由服务器添加到 Response Header 中,浏览器再次请求该资源时,会在 Request Header 中添加 if-none-match 头,值为上次 etag 的值,服务器收到请求后,会对请求资源再次作相同的摘要算法,和 if-none-match 值进行比对,若是不同,说明资源更新了,返回 200 以及更新后的资源文件,若是相同,说明文件没有被修改,则返回 304,由浏览器返回缓存资源。
总结来讲,last-modified / if-modified-sice 和 etag / if-none-match,就是将服务器返回的某一个值,由浏览器在发送请求的时候带回去,服务器拿到值后和本地文件的某个属性进行判断,来决定是否返回新的资源,仍是由浏览器返回缓存资源,这个过程,就叫作协商缓存。
相似于 expires 和 cache-control,etag / if-none-match 的优先级要比 last-modified / if-modified-since 高。
若是什么缓存策略都没有设置,那么浏览器会采用一个启发式的算法,一般会读取 Response Header 中的 date 头,减去 last-modified 值的 10% 做为缓存时间。
学习了上面的缓存策略,在实际场景中咱们该如何应用呢?