全国免费咨询:

13245491521

VR图标白色 VR图标黑色
X

中高端软件定制开发服务商

与我们取得联系

13245491521     13245491521

2023-08-15_我终于搞懂了http缓存!

您的位置:首页 >> 新闻 >> 行业资讯

我终于搞懂了http缓存! 点击关注公众号,技术干货及时送达 前言做了前端之后才发现,浏览器缓存真是一个神奇的存在,让你又爱又恨,又好奇又敬畏。 在开发过程中,相必各位前端开发经常会有这样的对话: 测试:这个展示有bug 前端:我本地是正常的啊,你清下缓存试试 其实一直以来我对缓存这一块知识都很模糊。前段时间用户有一个问题,后面清了缓存才可以。产品就问我能否上线之后不需要清除缓存,也不是很重要,让我看看能不能搞,不能搞就算了。当时对缓存比较好奇,手下也稍微有点空,就钻研了一段时间。 但是我查到的知识和我们的现有的服务完全匹配不上。理论和实践不对等,这让我对很多东西都更加疑惑。然后,,,然后我就放弃了这个令我望而生畏的领域。产品没再提起这个需求,我也再没看,嘻嘻。 然后呢,最近开发需求效率比较高(闲了起来),想着再啃一啃这块内容,才有了这篇文章。 通过这篇文章你可以了解以下知识:(以下的浏览器只针对谷歌浏览器) 缓存整个流程(不包括代理服务器缓存,问就是我没有条件和时间测试,后续遇到了再看看~)http的强缓存和协商缓存强缓存和协商缓存出现的必要条件以及如何清除磁盘缓存和内存缓存浏览器也有自己的强缓存?为什么明明服务nginx配置了响应头,但是实际请求的响应头并没有出现请求头配置可以解决缓存问题吗?使用了微前端-qiankun后发版必须要清缓存才生效吗?原因是什么缓存流程drawio 以上示意图仅出现针对未对服务器做特殊配置(如添加expires/Cache-Control等),若修改了服务器配置以下会有另外说明 重点文字说明 对于同一个url资源,不管服务器有没有更新资源,只要浏览器缓存时效未过期,都不会主动向服务器重新请求的 缓存类别强缓存强缓存一般是存储在本地的磁盘/内存中,名中强缓存之后的请求的特点: 1. 没有请求头只有响应头,这个响应头是第一次存储在浏览器的数据 2. 状态码一般是200,但是带有备注(来自磁盘缓存/来自内存缓存) 命中强缓存必要条件(任意一条即可) Cache-Control: max-age=xxx响应头有Expires响应头存在ETag和Last-Modified(协商缓存)且 不存在Cache-Control:no-cacheCache-Control: max-age和Expires两者都是规定强缓存存在的时间,命中强缓存规则,除了浏览器自己有一套缓存规则外,服务器可以返回这两个标头来干扰浏览器的缓存规则。 也就是说当响应头存在Cache-Control: max-age或Expires,浏览器会按照这个时间来当做强缓存存在的时间。一旦过了这个时间,浏览器就得重新向服务器发起请求。 如果响应头没有存在Cache-Control: max-age或Expires,但是有Last-Modified/etag,浏览器会按照自己的默认缓存规则来(后面会说这个) 如果响应头没有存在Cache-Control: max-age或Expires,也没有Last-Modified/etag,浏览器每次都必须发送新的请求,因为缓存失效之后是否可以使用缓存,还是需要Last-Modified/etag。 max-age 指定缓存的最大寿命时间,单位是秒。相对于请求时间,例如max-age=3600表示可以缓存3600秒。Expires 指定一个绝对的缓存过期时间,response发送时间为基准。例如Expires: Thu, 01 Dec 2022 16:00:00 GMT如果两者冲突(在http规范中) Cache-Control的优先级更高。max-age会覆盖Expires的设置。以max-age为准。如果你在nginx中这样配置呢? expires 60s; add_header Cache-Control max-age=10;最终会按照哪一个时间处理呢?答案是60s 你就要问了,你这矛盾啊,不是说Cache-Control的优先级更高嘛? 实际上nginx配置的那个expires指令,会同时转换成http的Cache-Control和Expires头 在这个响应头中,Cache-Control和expires确实都存在,但是Cache-Control有一个值是60,所以最终还是按照60s处理 磁盘缓存和内存缓存强缓存其实也就是浏览器缓存,但是浏览器缓存存储位置有两个:磁盘缓存和内存缓存,两者有所不同: 存储介质不同磁盘缓存存放在硬盘上,容量更大但速度较慢,加载时间相对较长。内存缓存存储在内存中,速度快但容量小,加载时间短。缓存对象选择不同缓存对象选择不同磁盘缓存偏向用于大文件,如图片、视频等。内存缓存偏向用于文本类、小文件,如CSS、JS等。时效不同磁盘缓存可以持续保存,关闭浏览器后不会消失。内存缓存只存在于当前会话,关闭页面即被清除。控制方式不同磁盘缓存需要依赖服务器响应头控制缓存策略。内存缓存不依赖响应头,由浏览器自主管理。空间限制不同磁盘缓存空间较大,但也存在存储限制。内存缓存空间受到内存大小限制。个人觉得对于开发而言,没有必要纠结代码到底是存在磁盘还是内存,仅做了解就可以。所以有关这部分我也没有过多去测试验证。 如何清除跳过强缓存上面说了,强缓存是有磁盘缓存和内存缓存的 清除内存缓存 关闭会话 强制刷新 清除浏览器缓存 服务器配置相应响应头 清除磁盘缓存 物理手段只能通过清除浏览器缓存(对,没错,就是你想的在浏览器设置清除缓存)来跳过 服务器配置相应响应头 为什么响应头没有max-age/Expires,浏览器也命中了强缓存浏览器其实本身就有自己的缓存规则,当没有服务器的干预情况下,一般都会按照浏览器的缓存规则来做一定的缓存处理,,且这个强缓存存在的时间是:(【文件访问时间】-【文件修改时间】)/10 image.png该测试中,文件上次修改的时间为10:21:40 , 文件第一次访问的时间10:47.01,文件第二次访问的时间(强缓存已失效)10:49.46 所以【预计强缓存存在时间】= (10:47.01-10:21:40)/10 约= 155.2s 【实际强缓存存在时间】 = (10:49.46-10:47.01)/10 约= 165s 且因为强缓存刚好失效的时间点我没有办法刚好访问到,所以【实际强缓存存在时间】是会比【预计强缓存存在时间】大一些,这个是在正常的误差范围。 协商缓存协商缓存是指【浏览器】和【服务器】之间的协商,一般从服务器下载的资源会比较大。但是如果是协商缓存,服务器并不会直接把资源给你,而是告诉你可以从缓存拿取数据,这样响应数据比较小,加载时间更短。 协商缓存的工作流程客户端发送带验证字段的conditional请求服务器端接收请求,比对资源验证信息如果资源未修改,返回304状态告知继续使用缓存如果资源已修改,返回200状态和新的资源协商缓存存在的标志是:服务器端返回包含资源标识的验证字段,如Last-Modified或ETag 如何跳过协商缓存强制刷新清除浏览器缓存服务器配置Cache-Control:no-store 或者在服务器端去掉Last-Modified或ETag响应头 去掉Last-Modified或ETag响应头nginx配置:add_header Last-Modified "";# 关闭 ETagetag off;ETag和Last-ModifiedLast-Modified 表示资源内容上次被修改的时间浏览器使用If-Modified-Since请求头进行验证时间精确到秒级,可能存在冲突ETag 是内容摘要,能唯一标识版本浏览器使用If-None-Match请求头验证以Hash形式展现,不会暴露真实文件路径协商缓存的整个过程描述第一次请求服务器 浏览器请求不包含任何验证字段服务器返回200状态码、Last-Modified时间戳、ETag签名和完整资源浏览器保存资源内容,以及Last-Modified和ETag值第二次请求资源 浏览器带上If-Modified-Since(值为上次服务器返回的Last-Modified)和If-None-Match(上次服务器返回的ETag)请求头服务器收到请求后,对比当前资源文件的最后修改时间 是否等于 If-Modified-Since 以及资源文件的ETag 是否等于 If-None-Match服务器根据对比结果,ETag和Last-Modified两个同时对比如果两个结果都是相等,表示资源未修改,返回304告知浏览器继续使用缓存如果两个结果都是不等,表示资源已修改,返回200状态码和新的Last-Modified、ETag以及资源内容如果两个结果出现冲突,已ETag的计算结果为准返回给浏览器浏览器处理服务器结果收到304则继续使用缓存收到200则丢弃旧缓存,保存新Last-Modified、ETag和资源内容简单粗暴白话文请求头If-Modified-Since 和服务器自己计算的Last-Modified ,俩值必须一样,不一样就返回新的资源 请求头If-None-Match 和服务器自己计算的ETag ,俩值必须一样,不一样就返回新的资源 请求头If-Modified-Since 的值是上一次服务器计算之后的Last-Modified ,返回给浏览器的,浏览器保存之后用于下一次请求 请求头If-None-Match 的值是上一次服务器计算之后的ETag 返回给浏览器的,浏览器保存之后用于下一次请求 drawio 代理服务器缓存日后再议~ 有关响应头具体有哪些响应头可以干涉强缓存/协商缓存的?看下面: 强缓存 Cache-Control: max-age - 设置最大缓存时效(强缓存的时效)Cache-Control: no-cache - 需要对缓存进行验证(禁用强缓存)Cache-Control: no-store - 禁止缓存(强缓存和协商缓存都禁用)Expires- 设置绝对过期时间(强缓存的时效)Pragma: no-cache- 兼容HTTP 1.0的不缓存指令协商缓存 Last-Modified - 资源上次修改时间,用于If-Modified-Since验证ETag- 资源唯一标识,用于If-None-Match验证缓存范围 Cache-Control: public - 允许客户端和CDN缓存Cache-Control: private - 只允许客户端浏览器缓存Cache-Control: s-maxage - 只允许CDN等代理服务器缓存其他 Vary - 区分不同版本的缓存Age - 代理服务器上的缓存时间Expires - 控制缓存失效时间客户端请求头在解决强缓存期间,我看了很多文章都说需要在请求头加上Cache-Control: no-cache就可以,但是实际实践的结果并不理想。浏览器还是老样子,并没有按照请求头指令执行。 后面看了其他资料,了解到其实请求头的指令,浏览器会当做建议处理,实际上影响浏览器缓存的因素过多(像文件修改时间,文件大小等),请求头只是一个建议而已,浏览器不一定会采取的。可以不负责任的说: 服务器就是爷爷,浏览器是爸爸,客户端请求头就是儿子。 儿子的建议,爸爸不一定会采纳;但是爷爷的指令,爸爸一定会遵循。 所以如果想要清除缓存,建议还是在服务器端设置。 遇到的问题为什么nginx没有配置协商缓存有关指令,响应头还是自带了ETag和Last-Modified?对于静态资源文件如 JS、CSS、图片等,nginx默认情况下会自动生成Last-Modified和 ETag 响应头,这是由 ngx_http_static_module模块提供的功能。 原理是 nginx在响应静态资源请求时,会读取文件的元信息,然后设置: Last-Modified 为文件getmtime(最后修改时间)ETag为文件inode编号加上修改时间和文件大小的哈希值这可以让客户端有基于 Last-Modified 和 ETag的缓存验证机制。 但是强制缓存如 max-age、public等Cache-Control 指令还需要手动配置。 如果想完全关闭 Last-Modified 和 ETag 生成,可以主动在nginx的location块设置: // 第一种add_header Last-Modified "";# 关闭 ETagetag off; // 第二种add_header Cache-Control no-cache;nginx配置了Cache-Control: max-age,响应头为什么没有生效这个问题是在我们公司的服务上面确确实实遇到的问题,我们服务器配置了expires和 Cache-Control no-cache(咱也不知道为什么要把这俩属性配到一起),但是结果就是,实际上服务域名的响应头并没有这两个头头。 去找了运维小哥哥,他肯定地说,这个配置肯定生效了的,不然域名静态资源都获取不了。 我问他有咩有其他配置影响到了,他看了会说确实有。我们服务是有一个网关的,网关的配置覆盖了原有配置,导致之前的不生效。 所以如果nginx配置不生效,勇敢去刚运维。 使用了微前端-qiankun后发版必须要清缓存才生效这个是我们项目一直都有的问题,但是因为是后管系统,大家的容忍度比较高,没有对这一块的需求提出来。of cource,开发也不主动做(我很忙的了啦,后面再优化了啦)。 但是在学习这一块的时候,我发现了一个盲点: 欸?Vue不同版本打包的chunk的hash值是不一样的,那么为什么每次刷新还是加载的旧的chunk ? 哦,应该是index.html入口文件名称不变,所以导致里面加载的资源还是旧的。 ok,fine,为了验证我刷新了下页面,每次的index.html页面不是304就是200,并没有从缓存获取数据。也就是说每次的index.html都是从服务器获取的,那么为什么还会有旧的index.html? 这确实是一个好问题,刚开始我把这个锅归咎于运维(背锅侠)。后面仔细想想应该是浏览器端的缓存导致的问题,与服务器没有关系。所以我又开始陷入瓶颈中... 然后一次偶然的发版,我偶然刷新了子系统,发现子系统仅仅刷新就已经更新了最新版本的代码。但是基座域名下强制刷新也是旧的代码(从浏览器磁盘缓存获取的代码),必须得清浏览器缓存才可以。 也许你可能不了解什么是基座,什么是子系统。我可以这样举例来帮助你了解。 假设,基座:A域名; 子系统1:B域名; 子系统2:C域名; 子系统3:D域名 ,微前端的作用就是不管你访问的是哪个子系统,用户看到的域名永远都是A域名(基座域名) 但是如果你想要访问B域名的b菜单业务功能,你有两个选择: 从A域名进入,点击A域名的菜单栏的b菜单,这个时候微前端会加载B域名资源,所以你可以在A域名下看到B域名的功能从B域名进入,找到b菜单,加载业务功能这两种的根本不同在于,A域名中转加载B域名代码是通过fetch方式,而B域名是直接加载B域名代码,资源类型是document。 对浏览器来说,浏览器默认不会对 HTML 文档进行强缓存。每次请求文档时都会查询服务器获取最新版。 但是对于通过代码显式发送的请求,默认为 fetch请求。fetch 请求浏览器默认是有强缓存的。 结论: 通过基座方式加载子系统业务代码,有缓存,原因是请求子系统资源时,发送的为 fetch请求。fetch 请求浏览器默认是有强缓存的。 直接通过子系统加载业务代码,没有缓存,原因是请求的资源类型是document类型,浏览器默认不会对该类型文档进行强缓存。每次请求文档时都会查询服务器获取最新版。 解决: 总不能为了一个缓存,把整个微前端去掉吧。所以终归还是需要在服务器端配置去缓存的指令才可以。 总结需要想要用户不清除缓存使用新代码,找运维配置服务器对应响应头。 最后给大家推荐一个ai,类似于gpt但比gpt好用的https://claude.ai/ 如果文章对你有帮助的话欢迎「关注+点赞+收藏」~ 阅读原文

上一篇:2022-11-14_实战教程·元宇宙来了,准备好你的电子名片了吗?(一) 下一篇:2025-01-10_雷德利·斯科特:我常想,这电影我TMD是要怎么开头呢?

TAG标签:

17
网站开发网络凭借多年的网站建设经验,坚持以“帮助中小企业实现网络营销化”为宗旨,累计为4000多家客户提供品质建站服务,得到了客户的一致好评。如果您有网站建设网站改版域名注册主机空间手机网站建设网站备案等方面的需求...
请立即点击咨询我们或拨打咨询热线:13245491521 13245491521 ,我们会详细为你一一解答你心中的疑难。
项目经理在线

相关阅读 更多>>

猜您喜欢更多>>

我们已经准备好了,你呢?
2022我们与您携手共赢,为您的企业营销保驾护航!

不达标就退款

高性价比建站

免费网站代备案

1对1原创设计服务

7×24小时售后支持

 

全国免费咨询:

13245491521

业务咨询:13245491521 / 13245491521

节假值班:13245491521()

联系地址:

Copyright © 2019-2025      ICP备案:沪ICP备19027192号-6 法律顾问:律师XXX支持

在线
客服

技术在线服务时间:9:00-20:00

在网站开发,您对接的直接是技术员,而非客服传话!

电话
咨询

13245491521
7*24小时客服热线

13245491521
项目经理手机

微信
咨询

加微信获取报价