简单回顾一下前端缓存策略 ⛏️
- 强缓存:在请求头规定的max-age和expirse过期之前直接读取本地缓存的资源,如果已经过期则调用协商缓存
- 协商缓存:也叫弱缓存,以下都称为协商缓存。协商缓存是缓存过期,刷新缓存时间或者文件更新,返回新文件的缓存阶段。
- 以下是协商缓存的过程:
- 前端 首次 请求文件、静态资源时,请求响应头带上Etag/Last-Modified请求头
- 告诉浏览器下一次同样的资源请求需要在请求头带上If-None-Match:值为上一个请求响应头的Etag,If-Modified-Since:值为上一个请求响应头的Last-Modified,服务器根据请求头的这两个字段匹配资源是否变更
- 如果未变更,他们的值应该是一样的,返回304即可。如果变更了,值不一样,返回200和新的资源内容
- 所以非首次请求资源才会带上If-None-Match/If-Modified-Since
- 但是响应头每次都会带上Etag/Last-Modified(文件、静态资源会自动携带因为可能需要缓存,json字符串接口不会自动携带,普通接口不需要也不应该缓存)
新手请记住强缓存和协商缓存容易混淆的地方,强缓存和协商缓存不是非我即他的两种独立使用的策略。而是包含关系的缓存策略。强缓存开启一定包含协商缓存,因为强缓存资源到期需要使用协商缓存刷新强缓存的缓存时间
。- 协商缓存的策略使用也是使用Cache-Control: max-ag=0(或者设置为no-cache)将强缓存时间设置为0强制开启协商缓存。
强缓存和协商缓存可以理解为是前端缓存的不同阶段
需求背景 🔥
- 项目有一个全局请求配置文件的轮询接口,他会在发版时变更,轮询发现配置文件有变更则提示用户刷新页面,启用新版本。
- 但是有一天出BUG了,发版成功,但是客户端没有检测到版本变化。
- 打开Console控制台一看,原来接口被强缓存了,接口返回200(from disk cache),读取本地缓存文件肯定没有变化,检测不到发版更新。
开始爬坑 ✏️
开始发现问题是开启了强缓存,心想这还不简单,直接改为弱缓存解决,easy。
还是太年轻了🤡🤡🤡
- 查阅MDN将Cache-Control设置为no-cache即可开启协商缓存
- 这里注意一下,no-cache不是不使用缓存,是每次都去服务器验证一下资源是否变更,变更则返回200和变更后的资源,未变更则返回304使用缓存。no-store才是不使用缓存。
第一个坑:本地调试开启https没有合法证书缓存无效,本地改为http正常
-
查看请求,请求头加上了Cache-Control: no-cache,响应头也返回了Etag,但是没有返回304
-
但接口没有被缓存,依然每次直接请求服务器(304才是协商缓存,它返回了200)
-
第一个坑问题解决
最后发现是本地当时开了https联调第三方https的接口,但是没有本地关联证书,裸奔https而浏览器不会缓存没有合法证书的https请求后面一次发到测试上发现缓存是有效的,线上是https有合法证书然后本地去掉https,缓存也有效了,🤮吐血,浪费了我好多时间
第二个坑:Cache-Control: no-cache缓存无效,需要改为Cache-Control: max-age=0
总所周知,程序解决了第一个坑,总会有第二个坑等着你
-
查看MDN、谷歌、百度看网友示例,设置为no-cache都可以开启协商缓存,但是我这里第二次请求没有带上弱缓存的请求头(If-None-Match/If-Modified-Since)
-
如下图所示(非首次请求截图),Cache-Control为no-cache,响应头带上了Etag/Last-Modified,但是请求头没有带上弱缓存的请求头,接口直接返回200而不是304没有使用协商缓存
-
花了很多时间找不到答案,累了毁灭吧,又不是不能用,协商缓存可能也就快了一点点而已,折腾啥又不能涨工资,无所谓.jpg QAQ。
-
在我快要放弃的时候,我看到了可以试一下将Cache-Control设置为max-age=0,果然了就可以了
max-age表示在给定的时间内(单位秒)使用强缓存,直接读取本地缓存文件超过时间则请求服务器验证文件是否变更
-
虽然对了,但为什么no-cache不行,我看MDN和别人的代码示例都说no-cache是可以,我认为实际上应该也是可以的,可能是中间网关做了什么处理?我也尝试过是否开启了Disable cache,但是我一直都是关的。百撕不得其姐,有没有懂哥赐教🫡(respect)!
问题来了,最开始的代码根本没有配置Cache-Control怎么默认开启了强缓存
- 又发现了一个奇怪现象,当我将Cache-Control请求头去掉,还原场景。获取该配置文件的接口刚开始还是304好好的,然后穿插了几个200强缓存(而且后面强缓存还变成连续的),然后又变成了304到底怎么回事?(该请求轮询时间是30s)
- 最后在知乎找到了答案,明明没有配置任何缓存策略,浏览器还是给我强缓存了?
- 大概意思是没有配置任何缓存策略时会启用
启发式缓存
,则缓存时间cacheTime为响应头返回的(Date-lastModified)*0.1
,所以就会一开始都是304因为cacheTime很小马上就过期了(不够30s),当cacheTime随着时间推移(资源未被更改)会越来越大,超过轮询的30s就变成了200强缓存。然后变成2个连续的200强缓存。当这个资源10天都未更改,那么这个请求一天都是强缓存,然后造成了项目发版了,但是没有发版提醒的BUG。 - 前端福尔摩斯(自封)🧐
结论 🏆
- 协商缓存无效可能是本地开启了https,改为http
- 设置Cache-Control: no-cache可能无效,使用Cache-Control: max-age=0
- 此次爬坑其实对业务来说无关痛痒,因为根本都没有遇到性能问题,这点性能提升对用户来说也是无感知的,但是此次爬坑让我对前端的强弱缓存有了更深的了解,之前我的理解前端的强弱缓存因为需要依赖后端返回的Etag、Last-Modified字段,所以应该需要后端或者负责Nginx配置的运维去负责管理的,原来前端也可以进行控制。也算是不枉费自己瞎折腾吧。