[网络]带你了解前端网络缓存

126 阅读6分钟

最近想重新系统地整理一下前端的知识,因此写了这个专栏。我会尽量写一些业务相关的小技巧和前端知识中的重点内容,核心思想。

前言

缓存是前端性能的一个高效方案,浏览器可以跳过请求资源这个步骤,直接从本地加载资源,大大加快了页面导航的流程。善于利用缓存是一个优化用户体验的方式,每个开发者都应该掌握。

前端缓存由分网络缓存(http缓存)和浏览器缓存2种。今天我们来了解一下前端的网络缓存。

缓存机制

首先浏览的缓存利用流程是这样的:

  1. 当导航开始之后,第一步就是判断是否有缓存
  2. 浏览器先根据强缓存判断,如果有缓存就直接使用缓存。
  3. 如果强缓存没有通过,则根据协商缓存判断。
  4. 如果浏览器返回304,则利用本地缓存
  5. 如果浏览器正常返回资源,则加载网络资源,渲染页面

强缓存

根据上方说明,我们知道浏览器会优先判断【强缓存】。事实也是如此,强缓存的优先级比协商缓存高,假如强缓存通过后,浏览器会无视,协商缓存直接利用本地缓存资源。

强缓存的标识是2个响应头字段cache-control和Expires。

Expires

Expires是http/1.0的内容,它的值是一个具体的时间。也就是说,浏览器第一次请求到页面资源之后,服务器会返回一个过期(Expires)时间。这样下次浏览器再请求之前,可以根据这个过期时间,知道资源过期了没有,如果没过期就利用缓存。

// Expires的语法
Expires: <http-date>
// Expires: Wed, 21 Oct 2015 07:28:00 GMT

cache-control

大家仔细想想就发现,Expires这种方式是有问题的。因为浏览器在二次请求的时候,判断的过期时间是由服务器给的。但是客户端的时间跟服务器的时间不一定是同步的!(用户可以手动修改本地时间)。这样的话,这个缓存过期时间就变得不严谨了。

因此http/1.1给我们带来了cache-control,可以说他是expries的一个修订版本。他的优先级比expries高,如果响应头中同时存在cache-control和expries。浏览器会优先以cache-control为准。 目前基本都用cache-control作为强缓存的标识,expires只是为了兼容一些老页面。

cache-control的进步点在于,他用的是一个时间段。服务器告诉浏览器页面资源的有效时期,在这段期间内,浏览器都可以用缓存。这样话就解决了时间不同步的问题,因此值只是一个时间段。此外它的配置内容也丰富了许多:

Cache-Control: max-age=<seconds>
Cache-Control: max-stale[=<seconds>]
Cache-Control: min-fresh=<seconds>
Cache-control: no-cache
Cache-control: no-store
Cache-control: no-transform
Cache-control: only-if-cached

更多可以参考:developer.mozilla.org/zh-CN/docs/…

协商缓存

cache-control确实解决了expires的问题,可是服务器不一定能准确地知道页面资源的下一次更新时间。如果只靠强缓存,就会出现另一个问题。假如服务器的资源下一次更新时间是在3天后,但cache-control设置的是1天。那么客户端在这3天内是有可能需要重新从浏览器获取同样的资源的。这显然不符合缓存的目的,因此【协商缓存】就是解决这个问题的,可以认为他是强缓存的补充。

在协商缓存的过程中,浏览器实际上已经把请求发出了,服务器在判断协商缓存之后,决定返回的是304还是资源。如果是304状态,浏览器就用缓存资源。

协商缓存的标识有Last-Modified / If-Modified-Since 和 Etag / If-None-Match。

Last-Modified & If-Modified-Since

Last-Modified 和 If-Modified-Since是配对使用的,同样是http/1.1的内容。

Last-Modified的原理是服务器在每次资源修改之后,记录一个时间。浏览器在请求这个资源的时候,服务器把这个时间发送给浏览器。这样浏览器在下次请求的时候,请求头就会带上If-Modified-Since值是上次服务器返回的Last-Modified的值。服务器根据资源更新时间与If-Modified-Since,作出协商缓存的结果。

// 示例
Last-Modified: Wed, 21 Oct 2015 07:28:00 GMT
If-Modified-Since: Wed, 21 Oct 2015 07:28:00 GMT

Etag & If-None-Match

Etag 和 If-None-Match是配对使用的,同样是http/1.1的内容。

Last-Modified根据资源更新时间判断看似是一个很好的方案了,但也会不完善的地方。在某些情况下服务器会定时给资源刷新,这样Last-Modified也会跟着更新,但实际上资源内容是没有改变的。所有如果我们直接用资源的修改标识来作协商的标识,就可以解决这个问题。Etag & If-None-Match就是这个方案。

Etag是根据资源文件的内容生成的,因此理论上只要资源内容没有修改,Etag就不会变。浏览器在请求资源时,服务器返回资源的Etag。下次浏览器再请求时,带上字段 If-None-Match ,值就是上次资源响应的Etag。服务器就可以根据 If-None-Match判断协商结果了。

// 示例
ETag: "33a64df551425fcc55e4d42a148795d9f25f89d4"
If-None-Match: "bfc13a64729c4290ef5b2c2730249c88ca92d82d"

注意Etag的优先级比Last-modified高,如果两者同时存在,默认以Etag为准。

强缓存与协商缓存的区别

强缓存的判断是在客户端本地执行的,也就是请求并没有发出去。而协商缓存是客户端与服务器的沟通过程,请求已经发出去了。如果协商缓存生效,服务器返回304状态码并没资源本身,所以这个请求体积很小,成本降到了最低。

怎么可以知道缓存有没有生效?

其实我们可以在浏览器的开发者工具的网络面板中看到数据是来自缓存还是请求回来的。

当size显示的是具体值,说明资源来自于服务器。如果显示cache字样说明来自缓存。这里显示的类型会有2种:

  • from disk cache 来自磁盘缓存
  • from memory cache 来自内存缓存

浏览器在保存资源缓存时,采用了2种方式。存在磁盘可以减少内存的占用,可以存储更久,但读取成本相对较高。而存在内存则十分高效,但需要消耗内存空间,存储时间不易太久。

浏览器在打开新标签的时候,会采用磁盘缓存。在没有关闭标签的情况下,刷新页面用的就是内存缓存。

图解

总结

今天我们了解了前端http缓存的知识,希望对大家有所帮助。在自己的产品中可以掌握使用缓存提高用户体验。

参考

www.jianshu.com/p/256d0873c…

developer.mozilla.org/zh-CN/docs/…

developer.mozilla.org/zh-CN/docs/…

developer.mozilla.org/zh-CN/docs/…

developer.mozilla.org/zh-CN/docs/…

developer.mozilla.org/zh-CN/docs/…