当前位置: 首页 > 后端技术 > Node.js

浏览器缓存策略

时间:2023-04-04 00:23:54 Node.js

本文github地址,欢迎star缓存是一种提高数据读取和读取性能的技术,广泛应用于软件开发中,如常见的CPU缓存、数据库缓存、浏览器缓存等。缓存是其中之一Web前端性能优化的必要手段。既能保证用户第一时间获取最新资源,又能减少网络请求。浏览器缓存策略主要有以下两种:HTTP缓存ServiceWorkerHTTP缓存HTTP缓存需要浏览器和服务端配置使用。例如,Nginx和Apache可以设置不同的HTTP缓存策略。HTTP缓存主要作用于浏览器的静态文件,分为强制缓存和协商缓存。强制缓存强制缓存策略是浏览器根据资源过期时间决定是否使用缓存。如果在缓存中找到该资源并且没有过期,则直接使用缓存资源,状态码为200,否则直接请求。强制缓存是根据Expires和Cache-control来决定的。ExpiresHTTP/1.0可以使用响应头字段Expires来设置缓存时间,对应一个未来的时间戳。expires:Thu,06Mar203112:35:43GMTExpires有一个致命的缺陷:它使用的时间是基于服务器的,但是浏览器是通过本地时间和这个时间比较来判断的,这会导致时间出现错误准确性。因此,为了更精确地控制资源,http/1.1增加了一个新的Cache-control响应头域。Cache-controlCache-control是一个新的http/1.1响应头字段,用于控制浏览器强制缓存。其常用值如下:字段描述no-cache表示使用协商缓存,即每次使用缓存前服务器必须确认缓存资源是否更新;no-store禁止浏览器和所有中间缓存存储响应内容publicpublicCache,表示可以被代理服务器缓存,可以被多个用户共享private私有缓存,不能被代理服务器缓存,以及不能被多个用户共享max-age的值以秒为单位表示缓存的有效时间must-revalidate当缓存过期时,需要去服务器验证缓存的有效性。该值可以组合使用:cache-control:public,max-age=31536000需要注意的是,Expires的max-age和cache-control优先级高于Expires,如果出现同时,浏览器会优先考虑max-age的值。协商缓存协商缓存策略是每次请求时发送一??个请求,服务器端比较资源决定是使用本地缓存还是新的资源。请求响应返回的HTTP状态为304,表示缓存仍然有效。控制缓存的挑战从浏览器端转移到了服务器端。协商缓存的控制方式有两种:Last-ModifiedandIf-Modified-SinceETag和If-None-MatchLast-ModifiedandIf-Modified-Since服务器需要通过比较资源来判断缓存是否过期。如果浏览器直接将资源发送给服务器进行比对,网络开销太大,也就失去了缓存的意义,显然不可取。有一个简单的判断方法,就是通过响应头域Last-Modified和请求头域If-Modified-Since比较双方资源的修改时间。工作流程如下:浏览器第一次请求一个资源,服务器在返回资源的响应头中添加一个Last-Modified字段,表示该资源在服务器上的最新修改时间。当浏览器再次向服务器请求资源时,请求头中会携带服务器返回的修改时间。此请求标头称为If-Modified-Since。服务器再次收到请求,根据请求头If-Modified-Since的值,判断相关资源是否发生变化,如果没有,返回304NotModified,不返回资源内容,浏览器使用资源缓存值;否则,正常返回资源内容,更新Last-Modified响应头内容。这种方法虽然可以判断缓存是否失效,但是有两个问题:精度问题,Last-Modified的时间精度是秒,如果在1秒内发生修改,缓存判断可能失效。精度问题,如果一个文件被修改然后恢复,内容并没有改变。这种情况下,浏览器的缓存可以继续使用,但是因为修改时间变了,重复的内容会再次返回。ETag和If-None-Match为了解决精度和准确度的问题,HTTP提供了另一种方式来准确判断缓存不依赖于修改时间,而是依赖于文件的哈希值,即response头域ETag和请求头域If-None-Match。工作流程如下:当浏览器第一次请求资源时,服务器会在响应头中添加Etag字段,Etag字段值为资源的哈希值;当浏览器再次向服务器请求资源时,会添加AddIf-None-Match,值为上次响应头域ETag的值;服务器再次收到请求,将请求头If-None-Match字段的值与响应资源的哈希值进行比较,如果两者的值相同,则说明该资源没有发生变化,并返回304NotModified;否则,正常返回资源内容,无论是否有变化,都会将计算出的hash值放入响应头的ETag域。这种缓存比较方式也存在一些问题,具体表现在以下两个方面。计算成本。与读取文件修改时间相比,生成哈希是一项昂贵的操作,尤其是对于大文件。如果要计算准确,需要阅读完整的文件内容。如果考虑性能,只读取部分文件内容,很容易出错。计算错误。HTTP没有规定哈希值的计算方法,不同的服务器可能使用不同的哈希值计算方法。这样做的问题是对于同一个资源,在两台服务器上生成的Etag可能是不同的,所以对于使用服务器集群处理请求的网站来说,使用Etag的缓存命中率会降低。注意:在协商缓存中,Etag的优先级高于Last-Modified。ServiceWorkerServiceworkers可以理解为充当Web应用程序、浏览器和网络(如果可用)之间的代理服务器。主要目的是实现离线缓存,它拦截网络请求并根据网络是否可用采取适当的行动并从服务器更新资源。它还为推送通知和访问后台同步API提供入口点。使用方法分为三步:注册并安装监听器。在使用ServiceWorker脚本之前,必须通过“注册”的方式加载。if('serviceWorker'inwindow.navigator){window.navigator.serviceWorker.register('./sw.js').then(console.log).catch(console.error)}else{console.warn('Browse浏览器不支持ServiceWorker!')}考虑浏览器兼容性,判断window.navigator中是否有serviceWorker属性,然后通过调用该属性的注册函数告诉浏览器ServiceWorker脚本的路径。浏览器获取ServiceWorker脚本后,会进行解析,解析完成后进行安装。安装可以通过监听“install”事件来监控,但是这个事件只会在第一次加载脚本时被触发。为了让脚本能够监听浏览器的网络请求,还需要激活脚本。脚本启动后,我们可以通过监听fetch事件来拦截请求,加载缓存资源。下面是上面“注册”的sw.js文件的内容:constCACHE_NAME='ws'//为什么这里是数组?因为我们可以监听多个资源letpreloadUrls=['/index.css']/*监听安装事件,安装事件一般用于设置浏览器的离线缓存逻辑*/self.addEventListener('install',function(event){/*该方法可以防止缓存完成并关闭serviceWorker*/event.waitUntil(/*创建一个名为CACHE_NAME的缓存版本*/caches.open(CACHE_NAME).then(function(cache){/*指定要缓存的内容,地址是相对于域名的访问路径*/returncache.addAll(preloadUrls);}));});/*注册fetch事件,拦截全站请求*/自己。addEventListener('fetch',function(event){event.respondWith(/*匹配缓存中对应的请求资源直接返回*/caches.match(event.request).then(function(response){//直接返回onmatch缓存资源if(response){Returnresponse;}Returncaches.open(cache_name).thenction(cache(cache){constpath=event.request.replace(seld.Location.(path)}).catch(e=>console.error(e))}));})这段代码先监听install事件,在回调函数中调用event.waitUntil()函数并传入一个Promise对象event.waitUntil来监听多个异步操作,包括缓存打开和添加缓存路径。如果其中一个操作失败,则整个ServiceWorker将无法启动。然后监听fetch事件,在回调函数内部调用函数event.respondWith()并传入一个Promise对象,当fetch请求被捕获时,会直接在event.respondWith函数中返回Promise对象的结果。在这个Promise对象中,我们使用caches.match来匹配当前的请求对象。如果匹配,则直接返回匹配的缓存结果,否则返回请求结果并缓存。使用限制ServiceWorker不能直接访问DOM,但可以通过postMessage接口发送的消息与它控制的页面进行通信;ServiceWorker只能在本地环境或者HTTPS网站中使用;ServiceWorker有作用域限制,一个ServiceWorker脚本只能作用于当前路径及其子路径;由于ServiceWorker是一项实验性功能,因此可能存在一些兼容性问题。总结Expires和cache-control的max-age优先级高于Expires。强缓存比协商缓存具有更高的优先级。在协商缓存中,Etag的优先级高于Last-Modified。ServiceWorker可以用来实现离线缓存。主要实现原理是拦截浏览器请求,返回缓存的资源文件。