

详解前端缓存,解决前端换包之后环境中仍会出现旧版效果 - 他好像一条狗啊
source link: https://www.cnblogs.com/jdWu-d/p/12918311.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

详解前端缓存,解决前端换包之后环境中仍会出现旧版效果 - 他好像一条狗啊 - 博客园
前端项目修改了很多东西:比如bug啊,样式啊。当你把前端项目打包之后满心欢喜的在 Nginx(测试环境)换上它,然后在 Jira 上修改bug状态@测试人员复测。然后测试人员开始找你battle了,你的bug怎么还是没修改啊,但是你明明换上了最新的版本,中间到底出现了什么问题。打开控制台的 network,显示如图所示。

问题就出在 from disk cache 这玩意上,也就是浏览器缓存,浏览器读取的还是缓存中旧版的资源,渲染出来的还是旧版的效果。除了 disk cache 外,还有其他几类浏览器缓存,总的来说,浏览器缓存大致分为4种,而这4种方式是有优先级顺序的,只有依次查找缓存且都没有命中的时候,才会去请求网络:
-
Service Worker:是一种独立于主线程之外的 Javascript 线程。它脱离于浏览器窗体,可以帮我们实现离线缓存、消息推送和网络代理等功能。
-
Memory Cache:存在内存中的缓存。包括当前中页面中已经抓取到的资源,例如页面上已经下载的样式、脚本、图片等。因为存储在内存中,MemoryCache 是响应速度最快的一种缓存,但由于同样的原因,缓存持续性很短,会随着进程的释放而释放,一旦我们关闭 Tab 页面,内存中的缓存也就被释放了。
-
Disk Cache:Disk Cache 也就是存储在硬盘中的缓存,读取速度慢点,但是什么都能存储到磁盘中,比之 Memory Cache 胜在容量和存储时效性上。会根据 HTTP Herder 中的字段判断哪些资源需要缓存,哪些资源可以不请求直接使用,哪些资源已经过期需要重新请求。
-
Push Cache:Push Cache 是 HTTP2 在 server push 阶段存在的缓存,当以上三种缓存都没有命中时,它才会被使用,Push Cache 是一种存在于会话阶段的缓存,当 session 终止时,缓存也随之释放。不同的页面只要共享了同一个 HTTP2 连接,那么它们就可以共享同一个 Push Cache。
-
其实常见的情况下只有 disk cache 和 memory cache,如下图博客园首页请求所示:

至于什么情况下是存在内存,还是存在硬盘。由于计算机内存有限,而且比硬盘容量小很多,浏览器会根据计算机具体情况来决定缓存放在内存中还是硬盘中。一般情况下,较大的 css 文件、js 文件和 jpg 图片这类大文件会被存入硬盘;当前系统内存使用率高的话,文件也会被优先存储进硬盘;而 Base64 格式的图片,几乎永远可以被塞进内存。
那为什么需要缓存呢,对前端来说,因为读缓存不需要发起请求,也就不需要考虑请求环境和速度,提高访问速度,用户体验大大提升;对于后端而言,也缓解了服务器的压力,减少网络 IO 消耗,减少带宽消耗。
但是什么时候需要缓存,什么时候不需要缓存。很明显,我这种换版操作肯定是需要重新获取新的资源的。最简单的解决办法就是 Ctrl+F5 强制刷新,强制告诉浏览器不获取缓存,必须重新去获取新的资源,但是强制刷新这种手动触发还是对用户体验不太友好。特别是我们做的后台管理系统,在图片很少的情况下,有没有办法每次换版之后都获取最新的资源。这个时候就要涉及浏览器的缓存策略了,常见的缓存策略有强缓存和协商缓存。其实浏览器的缓存策略都是通过设置 HTTP Header 来实现的。
强缓存
不会向服务器发送请求,直接从缓存中读取资源。状态码:200,显示 from disk cache 或 from memory cache。通过设置两种 HTTP Header 实现:Expires和Cache-Control。
1.Expires:值是一个时间戳,表示本地时间到这个设置的时间缓存就失效。这样一来 Expires 就是有问题的,受限于本地时间,我直接手动去把电脑端的时间改掉,都能导致缓存失效,所以更推荐使用 Cache-Control,或者二者搭配使用。
在 Nginx中配置写法如下:
# gif、jpg、jpeg、png、bmp、ico这类的资源会在30天后失效 location ~ \.(gif|jpg|jpeg|png|bmp|ico)$ { root /XXXX/xxxx; expires 30d; }
2.Cache-Control:优先级比 Expires 高,同时设置 Expires 和 Cache-Control 则后者生效。可以在请求头或者响应头中设置,并且可以组合使用多种指令:
- private(默认):只能在浏览器中缓存, 只有在第一次请求的时候才访问服务器,若有 max-age,则缓存期间不访问服务器
- public:可以被任何缓存区缓存,如:浏览器、服务器、代理服务器等
- no-cache:可以缓存,但每次都应该去服务器验证缓存是否可用,进入协商缓存阶段,若有 max-age,则缓存期间不访问服务器,
- no-store:不仅不能缓存,连暂存也不可以(即:临时文件夹中不能暂存该资源)
- max-age=<seconds>:以秒为单位的缓存时间,max-age=60,表示缓存60秒后失效,60秒内再次访问该资源,均使用本地的缓存,不再向服务器发起请求
- s-maxage=<seconds>:同 max-age 作用一样,只在代理服务器中生效(比如CDN缓存),s-maxage 优先级高于 max-age,只对 public 缓存有效。设置了 s-maxage,没设置 public,代理服务器也可以缓存这个资源
- must-revalidate:可缓存但必须再向源服务器进确认
- proxy-revalidate:要求中间缓存服务器对缓存的响应有效性再进行确认
在 Nginx 中配置写法如下,随便举一个指令:
location ~ .*\.(css|js|swf|php|htm|html )$ { add_header Cache-Control no-store; }
协商缓存
当 Cache-Control 和 Expires 过期或者它的属性设置为 no-cache 时(即不走强缓存),那么浏览器第二次请求时就会与服务器进行协商,服务器端会对比判断资源是否进行了修改更新,对比结果无非是以下两种:
-
- 如果服务器端的资源没有修改(Not Modified),那么就会返回304状态码,告诉浏览器可以使用缓存中的数据。
- 如果数据有更新就会返回200状态码,服务器就会返回更新后的资源并且将缓存信息一起返回。
至于浏览器是怎么和服务器交互,主要是依靠跟协商缓存相关的header头属性:Last-Modified/If-Modified-Since、ETag/If-None-Match。这些属性在请求头和响应头是成对出现的。
1.Last-Modified/If-Modified-Since:
浏览器在第一次访问资源,或缓存过期后访问,服务器返回资源的同时,在 response header 中添加 Last-Modified 的 header,值是这个资源在服务器上的最后修改时间,浏览器接收缓存文件和header信息。随后我们每次请求时,浏览器会自动带上一个叫If-Modified-Since 的时间戳字段给服务器,它的值正是上一次 response 返回给它的 Last-modified 值,然后服务器会根据 If-Modified-Since 的值对比资源的最后修改时间判断资源是否进行了修改更新。


2.ETag/If-None-Match :
Etag是由服务器为每个资源生成的唯一的标识字符串,这个标识字符串是基于文件内容编码的,只要文件内容不同,它们对应的 Etag 就是不同的,因此 Etag 能够精准地感知文件的变化。Etag 和 Last-Modified 类似,当首次请求时,我们会在响应头里获取到一个最初的标识符字符串。那么下一次请求时,浏览器就会在请求头里就会带上一个值相同的、名为 if-None-Match 的字符串供服务器比对。Etag 的优先级会比 Last-Modified 高,但是Etag因为要生成,也会更消耗服务器性能。


查看 Nginx 更新日志可知,在2014年6月26日就默认开启 Etag,对应的版本为1.7.3,也就是说1.7.3及以上的版本的 Nginx 默认开启 Etag。不过需要注意的是,如果 Nginx 有开启 Gzip,可能会与 Etag 有冲突。

然后就是各家的 Etag 生成情况都不太一样,取决于服务器的类型或配置的算法。以下是简书首页随便的一个请求,这个不是什么大问题,顺便提一嘴。

说了这么多,前端缓存最理想的效果就是希望能尽可能多的命中强缓存,对于频繁变动的资源,多使用协商缓存,同时,能在更新版本的时候让浏览器的缓存失效。这就要求了我们对资源进行 Nginx 配置的时候,对资源失效时间有个自己的衡量和把握。
最后,还有一种情况是浏览器在几次刷新过程中会出现新版效果,也会出现旧版效果,新旧交替。那就得考虑前端项目是否布置了多节点,并使用 Nginx 配置负载均衡了,如果是这个问题的话,只要全部 Nginx 节点环境都换上新打的前端包问题就迎刃而解了。
__EOF__
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK