首发地址: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,不能被代理服务器缓存,即私有缓存;反之可以,即公有缓存。
【扩展】
- 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
,即第一次被引流到哪个机器,之后就会一直访问该机器; - 先对
静态资源在集群中全量发布
结合webpack的优化建议
- 一般对资源打包时,文件命名都会加上hash值,保证了内容的变更就会使资源url变更,继而浏览器就不会命中缓存,发起请求获取新资源。那么可以对这类资源设置较长的缓存时间,一般为:
cache-control:max-age=365*24*60*60
; - webpack优化建议,对代码进行合适粒度的拆分,也就是降低缓存粒度,代码变更时,影响面较小;
- 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】用户行为对缓存使用的影响
-
地址栏回车/前进/后端
:不管缓存是否过期,尽量先使用缓存。 -
F5
:协商缓存 -
Ctrl + F5
:强制不使用缓存,获取新资源(请求头加parama:no-cache;cache-control:no-cache
)
更多技术分享,欢迎扫码关注~
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。