当前位置: 首页 > Web前端 > HTML

面试常客:HTTP缓存

时间:2023-03-27 22:49:51 HTML

速度,速度,还是速度。一个网站要想体验好,就必须在第一时间以最快的速度展示出来。mysql查询慢,所以加了一层redis作为缓存,网站资源加载慢,怎么办,使用HTTP缓存从HTTP/1.0开始就有HTTP缓存,目的是减轻服务器压力,加快网页响应速度缓存操作的目标HTTP缓存只能存储GET请求的响应,而不能对其他类型的请求做任何事情。缓存发展历史HTTP/1.0提出了缓存的概念,即强缓存Expires和协商缓存Last-Modified。后来HTTP/1.1有了更好的解决方案,就是强缓存Cache-Control和协商缓存ETag。为什么Expires和Last-Modified不适用?expires是过期时间,但是问题是这个时间点是服务器的时间。如果客户端的时间和服务器的时间有差异,就会不准确。所以改用Cache-Control,它表示过期时间,没有歧义Last-Modified就是最后一次修改时间,它能感知的单位时间是秒,也就是说1秒内变化多次,内容文件虽然变了,但是显示还是和以前一样,有不准确的场景,所以就有了ETag,可以通过内容来标记资源,判断资源是否变了1.1Cache的比较-ControlETag两种缓存类型上一节介绍了不同版本下的缓存类型。当时有强缓存和协商缓存的说法,但是没有具体介绍。现在让我们来谈谈这两种缓存类型。强缓存Cache-ControlHTTP/1.1通过过期时间来控制缓存。对应的字段有很多,比如max-age,比如Cache-Control:max-age=3600,表示缓存时间是0秒,过期失效缓存请求命令:Cache-Control:max-age=Cache-Control:max-stale[=]Cache-Control:min-fresh=Cache-control:no-cacheCache-control:no-storeCache-control:no-transform缓存响应指令:缓存控制:必须重新验证缓存控制:无缓存缓存控制:无存储缓存控制:无转换缓存控制:公共缓存控制:私有缓存控制:代理重新验证缓存控制:最大年龄=<秒>关键点:Cache-control:no-cache跳过当前的强缓存,发送HTTP请求(如果有协商的缓存标识,则直接进入协商Cache阶段)no-cache与max同义-age=0,即跳过强缓存,强制刷新Cache-control:no-store不使用缓存(包括协商缓存)Cache-Control:public,max-age=31536000为generally用于缓存静态资源public:响应可以被中间代理、CDN等缓存private:专用于个人缓存,中间代理、CDN等可以缓存该响应max-age:单位为秒更多说明参考使用说明ExpiresHTTP/1.0语法:Expires:为过期时间,存在于服务器返回的响应头中。Expires:Mon,11Apr202206:57:18GMT表示资源在2022年4月11日6:0057点过期,如果过期会向服务器发送请求。如果它在缓存控制中如果在响应头中设置了“max-age”或“s-max-age”指令,那么Expires头将被忽略缺点:服务器时间可能与浏览器时间不一致当Expires更准确且存在时同时,Cache-Control的优先级高于Expires。Expires由HTTP/1.0提出,浏览器兼容性更好。Cache-Control是HTTP/1.1提出的,可以同时存在。当不支持Cache-Control时,浏览器会根据Expires协商缓存。协商缓存需要和强缓存配合使用。使用协商缓存的前提是设置强缓存。设置Cache-Control:no-cache或pragma:no-cache或max-age=0告诉浏览器不要加强缓存pragma是HTTP/1.0中禁止网页缓存的字段。它的值为no-cache与Cache-Control的no-cache作用相同。ETag/If-None-MatchHTTP/1.1为文件生成一个唯一标识,判断是否过期只要内容发生变化,这个值就会变化,以配合If-None-Match。ETag是请求服务器后返回给每个资源文件的唯一标识符。客户端会将这个标识符存储在客户端(即浏览器)中。下次请求时,会在请求头的If-Nono-Match中带上它的值。服务器判断If-None-Match是否和自己服务器上的ETag一致。如果一致则返回304,重定向跳转使用本地缓存;inconsistent,则返回200,将最新的资源返回给客户端,并带上ETag。更多说明参考说明百科Last-Modified/If-Modified-SinceHTTP/1.0最后修改时间,即最后修改时间用于判断是否过期。浏览器第一次向服务器发送请求后,服务器会在响应头中加入该字段配合If-Modified-Since。当客户端访问服务器资源时,服务器会将Last-Modified放入响应头中。即这个资源在服务端的最后修改时间,客户端缓存这个值,下次请求这个资源时,浏览器会检测请求头中的Last-Modified,所以加上If-Modified-Since,ifIf-如果Modified-Since的值与该资源在服务器的最后修改时间一致,则返回304,使用本地缓存进行重定向;如果不一致则返回200,返回最新的资源给客户端,有Last-Modified的缺点:虽然文件被修改了,但是最后的内容没有改变,所以文件修改时间还是会更新。有些文件的修改频率不到一秒,所以不适合以秒为粒度记录。部分服务器无法准确获取文件的最后修改时间。更多说明参考命令百科ETagVSLast-ModifiedAccuracyETag>Last-Modified。ETag用于通过内容来识别资源,判断资源是否发生变化,但Last-Modified不同,在某些场景下准确率会达不到要求。比如编辑了一个文件,但是文件的内容没有改变,缓存就会失效;或者1秒内多次修改文件,Last-Modified能感知到的单位时间是第二次表现Last-Modified>ETag。Last-Modified只记录一个时间点,ETag需要根据文件的具体内容生成哈希值。如果两者都支持,服务器会优先考虑ETag协商缓存的条件请求。上面说过,协商缓存就是在请求头中加上If-。None-MatchorIf-Modified-Since,这些请求头是什么,加了有什么用?强缓存就是通过特定的时间过期或者过期时间来控制缓存。这是个问题。如果部分文件被修改了,因为强缓存,浏览器还是会显示原来的数据,所以对于那些经常变化的数据不能使用强缓存作为缓存策略,所以就有了协商缓存,它告诉浏览器:通过文件更改缓存无效。使用前需要去服务器验证是否是最新版本?这样,浏览器需要连续发送两次请求进行验证:首先是HEAD请求获取资源的修改时间、hash值等元信息,然后与缓存的数据进行比较。如果没有变化,就使用缓存;否则,发送另一个GET请求。获取最新版本,但是这样两个请求的网络成本太高,所以HTTP协议定义了一系列以If开头的条件请求字段,专门用来检查验证资源是否过期,将两个请求结合起来成一个请求。.并且校验责任也交给了服务端If-Modified-Since:与Last-modified相比,是否修改过If-None-Match:与ETag相比,唯一标识是否一致If-Unmodified-Since:与Last-modified相比,是否修改过withLast-modified,是否修改If-Match:与ETag比较,匹配If-Range,其中最常见的有If-Modified-Since和If-None-Match。它们分别对应Last-Modified和ETag。第一个响应报文需要提前提供Last-Modified和ETag,然后第二次请求可以带上缓存中的原始地址来验证资源是否是最新的。如果资源没有改变,服务器将响应304NotModified。表示缓存还有效,浏览器可以更新一个有效期,然后什么时候用缓存,什么时候用强缓存,什么时候用协商缓存?首先,强缓存的权重大于协商缓存。当强缓存存在时,只能查看协商缓存;其次,HTTP/1.1中的缓存标识符大于HTTP/1;所以当Cache-Control存在的时候,看它,如果不存在,看Expires。如果强缓存设置为Cache-Control:no-cache,Cache-Control:max-age=0,pragma:no-cache,也就是告诉浏览器不要加强缓存,它会进入协商缓存。判断最后一个响应中是否有ETag。如果是,则在请求标头中使用条件请求If-None-Match发起请求。如果没有,则判断最后一个响应中是否有Last-Modified。如果是,则发起如果请求头中没有带If-Modified-Since的条件请求,说明没有协商缓存,直接发起HTTP请求。无论是带有If-None-Match还是If-Modified-Since的请求,都会返回状态(服务器会判断资源是否发生变化),如果是304,说明缓存资源没有变化,并使用本地缓存;如果是200,表示资源发生变化,发起HTTP请求,记住响应头中的ETag/Last-Modified。大致流程图如下:那么哪些资源应该使用强缓存,哪些资源应该使用协商缓存呢?像静态资源这种我们长期不会改变的资源应该使用强缓存,这个也不难理解;而我们经常修改的文件应该使用协商缓存。如果资源没有变化,那么当用户第二次进入时,该资源仍然会被使用,如果资源被修改,则用户进入并发起HTTP请求获取最新的资源。当我们访问网站时,如果我们注意的话,我们可以在F12中观察到一两件事。如图,我的五年前端和三年面试放在github服务器上,F12进入Network,可以看到返回的header中的信息。Cache-Control、Expires、ETag和Last-Modified都存在于缓存位置。上面说过,无论你使用强缓存还是协商缓存,它们都会从浏览器的本地存储中获取。那么浏览器的本地存储存在于何处,它们是什么?分类呢?根据缓存位置的分类,分为四个地方,MemoryCache(内存缓存),DiskCache(硬盘缓存),ServiceWorker,PushCacheMemoryCache因为内存有限,并不是所有的资源文件都会缓存在memory,主要是用来缓存与preloader相关指令的资源,比如。预加载器可以在解析js/css文件的同时请求下一个资源DiskCache的磁盘缓存上的缓存。在所有浏览器缓存中,磁盘缓存的覆盖范围最大。会根据HTTPHeader中的字段判断哪些资源需要缓存,哪些资源过期需要重新向服务端请求ServiceWorker的独立线程,借鉴了网络工作者。也就是让JS运行在主线程之外,因为和浏览器窗口是分开的,因为不能直接访问DOM,但是还是可以做很多事情的,比如离线缓存,ServiceWorkerCache消息推送网络代理,这PWAPushCache的一个重要实现机制就是pushcache,浏览器中的最后一道防线,HTTP2中的内容优先级:ServiceWorker-->MemoryCache-->DiskCache-->PushCache。实践中讲了那么多理论知识,但到了实战中就不知所措了。我怎样才能打破它?以上都是口头辩论,只有实践才能知道真相(以上都是面试辩论,只有实践才能证明)。目前前端项目都是用webpack或者类似webpack的工具库打包,在webpack中配置hash。前端方面缓存工作完成我们要实现的效果是:HTML:协商缓存CSS、JS、图片等资源:强缓存,文件名带hashwebpack中的hash分为三种:hash、chunkHash、contentHashHash:与整个项目的构建相关,只要改变项目文件,整个项目的hash值就会发生变化chunkHash:与webpack打包的chunk相关,不同的entry会产生不同的chunkHash值根据文件内容定义hash,文件内容不变,则contentHash不变。CSS需要用contentHash处理,其他资源用chunkHash处理。非前端工程项目,即传统的前端页面,一般都放在静态服务器中,所以必须对修改的文件进行版本控制,比如在入口处添加版本号(index-v2.min.js)或时间戳(time=1626226)到文件index.js作为缓存策略。设置缓存策略,告诉浏览器是否缓存。这里我们做一个强缓存和协商缓存的demo来实验。强缓存方案的代码如下:constexpress=require('express');constapp=express();varoptions={etag:false,//禁用协商缓存lastModified:false,//禁用协商缓存setHeaders:(res,path,stat)=>{res.set('Cache-Control','max-age=10');//强缓存超时为10秒},};app.use(express.static((__dirname+'/public'),options));app.listen(3008);PS:代码来源:图解HTTP缓存。测试的时候需要注意一下。缓存下无法检测到刷新页面,点击返回后可以有效协商缓存方案。代码如下:constexpress=require('express');constapp=express();varoptions={etag:true,//打开协商缓存lastModified:true,//启用协商缓存setHeaders:(res,path,stat)=>{res.set({'Cache-Control':'max-age=00',//浏览器不加强缓存'Pragma':'no-cache',//浏览器不加强缓存});},};app.use(express.static((__dirname+'/public'),options));app.listen(3001);效果如下:附上两个demo地址供大家参考。强缓存协商缓存总结为什么HTTP缓存,是为了分担服务器的压力,有什么手段可以让页面加载更快?HTTP的强缓存和协商缓存,强缓存适用于变化不大的资源(如导入库、js、css等),协商缓存适用于更新频繁的文件(如html)。什么是强缓存?基于HTTP/1.0中的Expires,但是不允许确实,HTTP协议升级到1.1后,使用新的标识符Cache-Control来代替,但两者可以同时存在,Cache-Control的权重更大。什么是协商缓存?在HTTP/1.0中,以Last-Modified为依据,即最后过期的修改时间,也是不准确的。HTTP升级到1.1后,将被新的标识符ETag取代。两者可以同时存在,而且后者的分量更大。是Expires还是Last-Modified是看时间点的。理论上是没有问题的,但是出现了问题,于是就有了新的解决办法。当强缓存存在时,浏览器会使用强缓存标识来缓存,当强缓存设置为无效时,浏览器会使用协商好的缓存来做缓存策略上面的,即使作者对HTTP缓存引用理解很深理解浏览器的缓存机制深入理解浏览器的缓存机制前端缓存最佳实践浏览器缓存的强大Node实践深入理解强缓存和协商缓存HTTP缓存浅析MDNwebdocs图解HTTP缓存