关于前端相关缓存

浏览器缓存机制

ajax请求之后,会把请求的url和返回的响应结果保存在缓存中,当下一次调用ajax发送相同的请求时,浏览器会从缓存中把数据取出来,这是为了提高页面的响应速度和用户体验,什么时候会出现这个现象呢,就是要这两次的请求url和请求参数完全一样的时候,浏览器就不会与服务器交互。

不同刷新的请求执行过程
  1. 浏览器地址栏中写入URL,回车浏览器发现缓存中有这个文件了,不用继续请求了,直接去缓存拿。(最快)
  2. F5:就是告诉浏览器,别偷懒,好歹去服务器看看这个文件是否有过期了。于是浏览器就胆胆襟襟的发送一个请求带上If-Modify-since。
  3. Ctrl+F5:告诉浏览器,你先把你缓存中的这个文件给我删了,然后再去服务器请求个完整的资源文件下来。于是客户端就完成了强行更新的操作.

缓存类型:

  • 服务端缓存:

    CDN缓存;
    代理器缓存(eg: Nginx);

  • 客户端缓存(浏览器缓存): 主要通过http中的header中的字段来控制

    强缓存:浏览器请求资源时当命中强缓存,则从缓存中读取资源。response中的Expires或者Cache-Control;
    协商缓存:强缓存未命中,则进行协商缓存,浏览器将请求发往服务器,服务器判断可以走缓存就返回304 Not Modified, 否则返回请求的资源。

强缓存

  • Expires:服务器返回有个Date,表示在此时间段内,资源缓存有效。浏览器请求资源时判断当前客户端时间是否晚于Expires,不晚则表示缓存有效,命中强缓存,读取缓存资源。

    Expirse是HTTP1.0提出的缓存处理方案,因为是通过客户端时间和服务时间进行比较,这样当服务器和客户端存在很大的时区时,就会导致缓存失效,同时也可以同过篡改客户端时间导致缓存失效或者错乱,不是很可靠。
  • Cache-Control: 配置如下
    // 表明资源可以被浏览器、CDN、代理服务器缓存等任何对象缓存
    Cache-Control: public 
    // 表明资源只能被浏览器缓存
    Cache-Control: private 
    // 表明禁止强缓存,只能走协商缓存
    Cache-Control: no-cache 
    // 相对时间,表示从第一次收到请求后多久秒内走缓存
    Cache-Control: max-age=<seconds> 
    // 跟max-age相同,但是在共享资源中(CDN或者代理服务器)起作用。私有资源时忽略此参数
    Cache-Control: s-maxage=<seconds> 
    

Cache-Control中max-age设置的是缓存有效的相对时间

  1. 浏览器第一次请求资源a.js,服务器返回资源以及response中的Cache-Control:max-age=??.

  2. 再次请求a.js时,判断这次resquest的时间是否在有效期内(不知道浏览器判断的时候是不是判断本地缓存最后一次修改的时间和当前的request的时间差??),是的化就命中缓存。

    因为max-age是客户端之间的相对时间判断,都是客户端时间进行比较,所以比Expires更有效,更安全,但是手动篡改客户端时间依然可以导致缓存失效 因此Cache-Control设置了max-age后就会覆盖Expires。

协商缓存

#response header
// 服务器返回资源最后修改时间,Date
Last-Modified
#resquest header
// 浏览器再次请求资源时携带If-Modified-Since字段。字段的值就是上次response的Last-Modified的值
If-Modified-Since
#或者
#response header
// 服务器返回MD5根据当前资源内容生成的一个摘要,资源内容不变,这个摘要的值就不会变
ETag
#resquest header
// 浏览器再次请求资源时携带If-None-Match字段。字段的值就是上次response的ETag的值
If-None-Match
流程如下:
  1. 浏览器第一次请求资源a.js,服务端返回资源a.js和Last-Modified。
  2. 浏览器再次请求资源a.js,同时request的header中携带If-Modified-Since,值就是上次返回的Last-Modified
  3. 服务器收到a.js的资源请求,通过request中的If-Modified-Since的时间和本地资源的最后修改时间进行对比,没有变化将返回304 not modified,浏览器读取本地缓存。修改时间变化了,服务器返回请求的资源,并在response的header中携带最新的Last-Modified。

ETag跟上面流程一样,只是比较的不是资源最后修改的时间,而是资源的内容。

服务器响应请求时,通过此字段告诉浏览器当前资源在服务器生成的唯一标识(生成规则由服务器决定)
If-None-Match:再次请求服务器时,浏览器的请求报文头部会包含此字段,后面的值为在缓存中获取的标识。服务器接收到次报文后发现If-None-Match则与被请求资源的唯一标识进行对比。

不同,说明资源被改动过,则响应整个资源内容,返回状态码200。
相同,说明资源无心修改,则响应header,浏览器直接从缓存中获取数据信息。返回状态码304.

但是实际应用中由于Etag的计算是使用算法来得出的,而算法会占用服务端计算的资源,所有服务端的资源都是宝贵的,所以就很少使用Etag了。

Last-Modified + If-Modified-Since的缺点:

单一的时间比较,如果修改了资源但是资源的内容没有发生变化,就会导致缓存失效,这个是很不应该的。

浏览器(客户端)缓存主要是用于缓存一些静态资源

CDN缓存

对于各个地方的用户,就存在有些距离远的用户访问的时候慢一点,所以CDN就是别人的分布在各地的专门存放缓存资源的地方,这样就可以让各地的用户访问服务的时候都是走的最近的CDN服务器。如果发现缓存失效,则请求真正的服务器,获取到资源后缓存在本地,并将资源返回给客户端。
CDN的全称是Content Delivery Network,即内容分发网络。是架设在客户端和服务器之间的Cache层,客户端请求资源时,具体CDN原理不是很懂,大致的意思就是CDN对域名解析进行了调整,然后返回一个CNAME,浏览器对CNAME进行解析后得到了CDN服务器的ip,CDN服务器上存在请求的资源就返回本地缓存,不存在则CDN服务器向真正的服务器发出请求,获得资源后缓存在本地,并返回给客户端,实现整个流程。
优点:CDN缓存主要起到客户端跟服务器之前地域的问题,减少延时,分流作用,降低服务器的流量,减轻服务器压力

为何我抵制使用缓存?

  • 错误地缓存数据其实是一件很糟糕的事情。尽你所能不要缓存数据,如果不得已而为之,一定要保证你正确地缓存数据。
  • 计算机科学中有两件难事:缓存失效和命名。缓存是一个棘手的问题。近年来,我经常见到因为缓存产生的错误导致了不必要的混乱和延迟。

    常见错误缓存

  • 启动时缓存
  • 过早地缓存:开发周期中的“早“
  • 缓存所有内容
  • 重复缓存
  • 无法删除的缓存

参考资料:
http://www.cnblogs.com/lyzg/p/5125934.html
http://imweb.io/topic/55c6f9bac222e3af6ce235b9
https://segmentfault.com/a/1190000006741200#articleHeader1
https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Cache-Control
https://juejin.im/post/5a37792f51882524c742be8b
https://juejin.im/post/5a1d4e546fb9a0450f21af23
为何我抵制使用缓存:https://juejin.im/entry/5884184f1b69e60058dc7fc6