一、温故
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,不能被代理服务器缓存,即私有缓存;反之可以,即公有缓存。
【扩展】
- no-cache
存储策略等效于http1.0中的Pragma:no-cache;max-age过期策略等效于http1.0中的expires;若同时存在,前者优先级更高。 - cache-control和pragma是通用头部字段,响应时设置缓存;请求时可表示是否使用缓存,比如浏览器启用
disable cache时,请求头中会加上:param:no-cache;cache-control:no-cache;。 - 一般禁止缓存的写法:
cache-control:no-cache, no-store,must-revalidate;(三者互补) - 如果没有设置缓存过期策略,浏览器会采取启发式算法计算缓存时间(与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: 资源在代理服务器已缓存时间
二、资源发布的性能优化&建议
- 常规的优化思路
- 为了避免不必要的请求,建议
对静态资源使用缓存; - 缓存带来更新问题(资源修改后,怎么通知浏览器获取最新资源?)
应对策略:
资源更新时改变资源链接,避免命中缓存。比如资源链接后加随机或版本信息作为参数;(html作为入口文件,最好不要被缓存,保证访问的都是最新的资源) - 然而每次发版就将改变所有链接,造成未改变资源同样需要请求资源
采用更精准的控制,
使用文件摘要hash处理后作为文件名,仅被修改文件的名称会变更,才需要重新请求最新资源 - 目前
静态资源常部署到cdn,就会造成发布方式和顺序的问题:- 如果先发布html页面,会导致用户获取最新静态资源时404;因此建议
先发静态资源; - 静态资源覆盖式发布,可能导致旧的html获取旧的静态资源404;因此最好使用
使用增量发布(非覆盖式) - 由于集群的使用
- 如果一个机器一个机器的发布,会造成,用户被引流到已发布的机器获取最新的html,但静态资源的请求被引流到其他未发布的机器,导致404;有两种解决方式:
- 对
ngix设置sticky,即第一次被引流到哪个机器,之后就会一直访问该机器; - 先对
静态资源在集群中全量发布
- 如果先发布html页面,会导致用户获取最新静态资源时404;因此建议
- 结合webpack的优化建议
- 一般对资源打包时,文件命名都会加上hash值,保证了内容的变更就会使资源url变更,继而浏览器就不会命中缓存,发起请求获取新资源。那么可以对这类资源设置较长的缓存时间,一般为:
cache-control:max-age=365*24*60*60; - webpack优化建议,对代码进行合适粒度的拆分,也就是降低缓存粒度,代码变更时,影响面较小;
- webpack动态加载的使用:
import(),提前获取,真正使用时直接从缓存读取
- 一般对资源打包时,文件命名都会加上hash值,保证了内容的变更就会使资源url变更,继而浏览器就不会命中缓存,发起请求获取新资源。那么可以对这类资源设置较长的缓存时间,一般为:
三、常见案例&注意点
以下会列举出一些常见的缓存相关的案例,提供遇到缓存问题时排查问题的思路。
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】用户行为对缓存使用的影响
地址栏回车/前进/后端:不管缓存是否过期,尽量先使用缓存。F5:协商缓存Ctrl + F5:强制不使用缓存,获取新资源(请求头加parama:no-cache;cache-control:no-cache)
欢迎关注公众号,不定时更新哦~