强缓存与协商缓存
以下问题分析为个人见解,如有错误还请指正!
因缓存遇到的问题
工作中遇到图片中的现象,每次重新部署后会遇到样式丢失的问题。
通过Chrome的控制台分析原因,发现vue scope增加的组件属性选择器data的值在dom上的与css文件中的值不一致。
???脑子里面第一闪过的是,怎么会出现这样子的情况,本地打包查看发现也是完全一致的情况
问题分析过程
通过对jenkins中打包文件的输出与浏览器加载资源问价的hash比对发现,浏览器控制台一直在使用缓存的资源
jenkins打包输出的runtime.js(css构建产生的hash值在这个运行时的依赖文件中存储)
浏览器加载的runtime.js的hash值
问题一:为什么会使用了缓存呢?
出现上述问题的情况步骤是:通过a标签的href跳转,浏览器地址栏输入url回车,F5或浏览器刷新按钮都会有这样子的问题。勾选禁用缓存强制拉取最新资源的情况是正常的,这也变相说明远程服务器的资源文件没有因为构建打包导致混乱。
我在网上查找了一下原因,大致如下(以下是再不打开控制台或者即使打开了控制台也没有勾选Disabled cache):
a标签的href跳转和浏览器输入url回车,浏览器会以最少的请求来获取网页内容。这句话变相告诉我们,只要强缓存没有过期,就用本地的缓存吧,也不用发送请求到服务器协商缓存有没有过期了。
Cache-Control:max-age=86400,也就是这个入口html文件有24小时的有效期。所以这就是为什么会看到上面runtime.js与jenkins构建的文件hash不一致。
然后问题又来了,既然a标签的href跳转和浏览器输入url回车是强制使用了本地缓存,那不是还有F5吗,F5会发起请求到远程服务协商缓存是不是有效吗?
是的,正常来说应该是服务器判断出资源已经更新了,然后返回一个200的相应并携带最新的资源的响应体信息。但是我们的服务在构建之后返回的依然是304(not modified),告诉浏览器服务器资源没有更新协商缓存通过了,所以浏览器使用的还是本地的缓存。
我又在网上查了一下。ETag虽然是作为资源文件的唯一标识,但是ETag存在强弱两种类型。强ETag是无论资源实体发生了怎么样的变化,值都会发生变化。弱ETage只用于提示资源是否相同,只有资源发生了根本的变化才会变化,弱Etag的表现形式就是前面有一个W/。然后我询问了一下AI,AI告诉我引入文件中的hash串发生变化也是根本变化,所以最根本的原因是服务器缓存策略ETag出现了问题,导致协商缓存通过了,一直使用的本地的缓存,才没有请求到最新的资源。
问题二:就算是使用了缓存,那只要构建版本是一致的版本,也不会出现DOM中的data属性值与css中的属性器data的值不一致的情况吧!
目前我只能大致猜测原因如下:
第一次构建完成之后,页面访问获取的都是最新的资源,本地会缓存当前版本的资源。
然后新版本构建完成后,打开页面(a标签href跳转),默认使用强缓存,现在runtime.js中依赖的css版本是就版本。这个时候直接打开页面样式是正常的,只是增加的内容或者逻辑不会变化,因为都是用的强缓存。但是开发者会勾选chrome控制台当中的Disbale Cache,这个时候再点击路由请求资源文件的时候发起的请求会如下图片。因为没有hash,所以请求的资源会是最新的资源,这样本地与远程拉取的资源就出现了版本错位
最后js文件执行挂载dom已经dom的属性data值的时候就与runtim.js中依赖的运行时的css资源的属性选择器不一样了。
从而导致了样式不能挂载到对应的dom资源中
本人项目jenkins构建完成后推送远端大致命令如下。
# 前面并不存在 ssh root@[远端ip] 'rm -rf [远端静态资源路径]/*'这样子的删除所有文件的命令
scp -r dist/* root@[远端ip]:[远端静态资源路径]
所以同名文件会覆盖,新文件会增加,旧文件不会删除。所以我的推测可能是会成立的。
加下本地复现流程
当前最新版浏览器和构建文件,可以看到现在文件的拉取是一致的
修改页面内容之后可以看到,css的hash值已经改变了,构建的js虽然没有hash值,但是内容也发生变化了
新版jenkins构建目标文件。因为css文件是整个文件夹推过去的,所以暂时看不到css的hash
本地href跳转之后,打开控制台禁用缓存然后跳转路由,我们看下请求的目标js文件是带有最新的改动的
然后我们看下控制台源代码文件就一目了然了,确实如上所说,导致的新旧资源错位了
个人见解
没有hash后缀的资源文件远端服务器返回时响应头中最好禁用缓存,即Cache-Control:no-store或者no-cache;这样子前者默认不会使用缓存,后者回到服务器进行协商缓存(前提是协商缓存机制无问题)。带有hash后缀的文件每次变换之后文件hash会变化,从而使得资源访问路径发生变化,继而请求到最新的资源文件(即使对于带有hash后缀文件的协商缓存机制出现问题)。
强缓存与协商缓存
缓存相关的http header
强缓存
header | 请求或响应头 | 描述 |
---|---|---|
Cache-Control | 响应头或请求头 | 指定缓存机制,优先级高于express |
Express | 响应头或请求头 | 指定缓存过期时间,兼容http1.0 |
Cache-Control | 机制值 |
---|---|
no-store | 禁用缓存,包括协商缓存 |
no-cahce | 相当于max-age:0,每次都要进行协商缓存 |
max-age | 缓存内容多久失效 |
public | 整个网路链路都可缓存(客户端,代理服务器等) |
private | 只能客户端进行缓存 |
协商缓存
header | 请求或响应头 | 描述 |
---|---|---|
ETag | 响应头 | 资源唯一标识符,存在强弱两种标识 |
If-None-Match | 请求头 | 携带ETag的值 |
Last-Modified | 响应头 | 资源最后一次修改的时间 |
If-Modified-Since | 请求头 | 携带Last-Modified的值 |
ETag/If-None-Match这个对的优先级高于Last-Modified/If_modified-Since。