首发地址:https://mp.weixin.qq.com/s/wV...


 对部分前端er,实际接触缓存的实操不多,属于既熟悉又陌生的技术点。笔者这里介绍了常用的缓存相关基础以及常见案例,日常情况下掌握这些即可。

为什么使用缓存?如何结合实际正确使用缓存?带着这些问题开始正文~

温故

1. cache-control: _no-cache | max-age | no-store | must-revalidate | public | private_

  • max-age:表示缓存的有效时间;
  • must-revalidate: 
    1. 缓存过期后,必须向服务器发起校验请求; 
    2. HTTP规范中指出,校验请求失败时是可以使用过期缓存,而must-revalidate针对这种特殊情况强约束:只要资源过期未通过服务器的校验,就不能使用失效缓存。
  • no-cache:会缓存,但每次都会先校验;等同于max-age:0,must-revalidate
  • no-store:不允许缓存资源;常见的如浏览器的【前进/后退】,即使设置no-cache同样会不做校验的使用缓存,因此最好是no-store,没有缓存自然就无法使用缓存。
  • public/private:区别是资源是否是客户的隐私敏感数据,是则使用private,不能被代理服务器缓存,即私有缓存;反之可以,即公有缓存。

【扩展】

  1. no-cache存储策略等效于http1.0中的Pragma:no-cache;max-age过期策略等效于http1.0中的expires;若同时存在,前者优先级更高。
  2. cache-control和pragma是通用头部字段,响应时设置缓存;请求时可表示是否使用缓存,比如浏览器启用disable cache时,请求头中会加上:param:no-cache;cache-control:no-cache;
  3. 一般禁止缓存的写法:cache-control:no-cache, no-store,must-revalidate;(三者互补)
  4. 如果没有设置缓存过期策略,浏览器会采取启发式算法计算缓存时间(与last-modified有关)。

2. ETag | Last-Modified 
    ETag的优先级比Last-Modifed高。

  • ETag:服务器资源的唯一标识 
    相关的请求头字段:if-match、if-none-match
  • Last-Modified:资源最近一次的修改时间。 
    相关的请求头字段:if-modified-since、if-unmodified-since
  • 精确粒度为秒,因此不适合短时间频繁变更的资源。
  • 由于目前都采用构建打包的方式,因此会出现,资源内容未变更,但文件的修改时间变更的情况。

3. 强缓存 / 协商缓存

  • 强缓存:expires、cache-control:max-age=100
  • 协商缓存:ETag、last-modified

4. 扩展字段

  • Date: 响应报文的生成时间。
  • Age: 资源在代理服务器已缓存时间

资源发布的性能优化&建议

常规的优化思路

  1. 为了避免不必要的请求,建议对静态资源使用缓存
  2. 缓存带来更新问题(资源修改后,怎么通知浏览器获取最新资源?) 
    应对策略:资源更新时改变资源链接,避免命中缓存。比如资源链接后加随机或版本信息作为参数;(html作为入口文件,最好不要被缓存,保证访问的都是最新的资源
  3. 然而每次发版就将改变所有链接,造成未改变资源同样需要请求资源 
    采用更精准的控制,使用文件摘要hash处理后作为文件名,仅被修改文件的名称会变更,才需要重新请求最新资源
  4. 目前静态资源常部署到cdn,就会造成发布方式和顺序的问题:
  • 如果一个机器一个机器的发布,会造成,用户被引流到已发布的机器获取最新的html,但静态资源的请求被引流到其他未发布的机器,导致404;有两种解决方式:
  • 如果先发布html页面,会导致用户获取最新静态资源时404;因此建议先发静态资源
  • 静态资源覆盖式发布,可能导致旧的html获取旧的静态资源404;因此最好使用使用增量发布(非覆盖式)
  • 由于集群的使用
  1. ngix设置sticky,即第一次被引流到哪个机器,之后就会一直访问该机器;
  2. 先对静态资源在集群中全量发布

结合webpack的优化建议

  1. 一般对资源打包时,文件命名都会加上hash值,保证了内容的变更就会使资源url变更,继而浏览器就不会命中缓存,发起请求获取新资源。那么可以对这类资源设置较长的缓存时间,一般为:cache-control:max-age=365*24*60*60;
  2. webpack优化建议,对代码进行合适粒度的拆分,也就是降低缓存粒度,代码变更时,影响面较小;
  3. webpack动态加载的使用:import(),提前获取,真正使用时直接从缓存读取

常见案例&注意点

    以下会列举出一些常见的缓存相关的案例,提供遇到缓存问题时排查问题的思路。 
1. 【案例1】常见的场景:部分用户打开页面点击某个文件链接下载时,发现下载的是旧资源

  • 分析:文件上传时,重新上传一份更新了内容相同命名的文件,资源虽然更新了但就浏览器而言,链接不变,在设置了缓存的情况下就会命中缓存使用旧资源。
  • 解决方案:1)临时方案:清空缓存,刷新页面重新下载即可 
    2)根治方案:保证上传文件名称的唯一性或在链接后加随机值参数(推荐前者),目的都是为了在资源更新的情况不命中缓存。

2. 【案例2】node工程本地测试时静态资源修改后,每次请求仍然显示旧资源

  • 分析1:考虑浏览器缓存作怪 
    解决方案:开启disable cache(开启后,每次请求头中都会加上Cache-control:no-cache;Parama:no-cache) 
    现象:仍然返回旧资源
  • 分析2:资源返回状态码200非缓存读取,说明问题不在浏览器,而在于服务端;但是public下的资源是最新的,那服务器从哪里读的旧资源并返回呢? 
    解决方案:显然服务器是读了内存缓存。工程采用了egg,config中static设置了buffer:true的情况,会把资源缓存到内存,避免每次请求再通过文件流读取文件;这里把buffer设置为false(默认false) 
    现象:问题解决。

    提示:该案例只是本地连测试环境调试需要更新资源后,得到最新反馈才需要关闭缓存,线上环境最好开启缓存。

3. 【注意点1】浏览器前进/后退,若存在缓存,即使缓存失效,也会直接使用,而不会和服务器校验。注意使用cache-control:no-cache也不能解决该问题,因为其等效于max-age=0,must-revalidate;,还是会缓存资源。

  • 解决办法:只有使用no-store才真正不缓存资源。

4. 【注意点2】用户行为对缓存使用的影响

  1. 地址栏回车/前进/后端:不管缓存是否过期,尽量先使用缓存。
  2. F5:协商缓存
  3. Ctrl + F5:强制不使用缓存,获取新资源(请求头加parama:no-cache;cache-control:no-cache

更多技术分享,欢迎扫码关注~
1584413667(1).jpg


夜暮sky
97 声望5 粉丝