0

    深入理解浏览器缓存机制

    2023.08.01 | admin | 130次围观

    单位 |北京事业群

    作者 |许晨熙

    编辑 | 小优

    一.前言

    首先我们要知道的是,为什么作为一个前端开发人员要深入理解浏览器缓存机制?因为浏览器缓存机制会直接影响前端性能问题。项目中使用好了缓存,对整体项目性能来说有很大的帮助。比如使用网页后退功能的时候,会发现网页加载的特别快,体验感非常好,这就是浏览器缓存给用户带来的好处。

    一个好的缓存策略可以缩短网页请求资源的距离,减少延迟,并且由于缓存文件可以重复利用,还可以减少带宽,降低网络负荷。

    对于一个数据请求来说需要三个步骤:发起网络请求、后端处理数据、浏览器响应数据。对浏览器来说,它可以从第一步骤和第三步骤对缓存进行优化处理。例如直接使用缓存而不发起请求,或者发起了请求但后端存储的数据和前端一致,那么就没有必要再将数据回传回来,这样就减少了响应数据时间,对用户来说可以很快从浏览器看到响应数据,用户体验感极佳。

    接下来的内容会通过缓存策略和实际场景应用缓存策略来探讨缓存机制。

    二.缓存策略

    我们知道浏览器与服务器通信的方式为应答模式,也就是:浏览器发起HTTP请求 – 服务器响应该请求,那么浏览器怎么确定一个资源该不该缓存,如何去缓存呢?可以分为如下步骤:

    •浏览器向服务器发送请求,请求资源;

    •服务器返回资源并通过响应头决定缓存策略;

    •浏览器根据响应头的策略决定是否缓存资源(假设为是),就会将响应头与资源一同缓存下来;

    •在浏览器再次请求并命中资源的时候,此时浏览器会检查上次缓存的缓存策略,根据策略的不同、是否过期等判断是直接读取本地缓存还是与服务器协商缓存。

    浏览器对于缓存的处理是根据第一次请求资源时返回的响应头来确定的。具体过程如下图:

    以上内容描述就引出了浏览器缓存的两种缓存机制,为了读者更容易理解,本文章通过是否需要重新向服务器发送http请求将缓存过程分为两大部分,即强缓存和协商缓存。

    强缓存

    强缓存主要使用Expires、Cache-Control 两个头字段,两者同时存在时Cache-Control 优先级更高。当命中强缓存的时候,客户端不会再请求,直接从缓存中读取内容,并返回HTTP状态码200。以下将对两header字段进行详细描述,帮助前端开发人员快速理解其作用。

    Expires:响应头,代表该资源的过期时间。此字段是http1.0提出的一个表示资源过期时间的header,它描述的是一个绝对时间,由服务器返回,但是基于此字段受限于本地时间,如果修改了本地时间,可能会造成缓存失效。

    当客户端请求服务器的时候,服务器会返回资源的同时还会带上响应头Expires,表示资源的过期具体时间,如果客户端在过期时间之前再次获取该资源,就不需要再请求服务器了,可以直接在缓存里面拿到数据。

    由以上描述我们可以得出:在过期时间以内的话,我们可以直接获取缓存资源,为用户节省了很多流量,同时也减少了服务器重复读取磁盘带来的压力;但也能看出来此字段有一个很大的弊端,就是它返回的是服务器的时间,然而判断的却是客户端的时间浏览器缓存机制有几种,假如用户改变客户端的时间,导致缓存时间判断不准确,进而缓存失效。因此也就引出了Cache-Control字段。

    Cache-Control:请求/响应头,缓存控制字段,精确控制缓存策略。此字段出现于http 1.1,优先级高于Expires,表示的是相对时间。

    Cache-Control既能出现在请求头又能出现在响应头,其不同的值代表不同的意思,下面我们具体分析下此字段常用参数设置。

    •max-age: 表示服务器端告知客户端浏览器响应资源的过期时长。

    •s-maxage:对于大型架构的项目通常会涉及使用各种代理服务器的情况,这就需要考虑缓存在代理服务器上的有效性问题,这边是s-maxage存在的意义,它表示缓存在代理服务器中的过期时长,且仅当设置了public属性值时才是有效的。

    •no-cache:不使用本地强缓存。需要使用缓存协商。

    •no-store:直接禁止浏览器缓存数据,每次用户请求该资源,都会向服务器发送一个请求,每次都会下载完整的资源,此属性与no-cache为互斥属性。

    •public:可以被所有的用户缓存,包括终端用户和中间代理服务器。·private:只能被终端用户的浏览器缓存,不允许中间缓存代理进行缓存,默认取值。

    由以上分析我们可以看出来,实际项目中,只要设置Cache-Control属性值完全能达到浏览器缓存的目的。于此得出的结论:在缓存有效期内命中缓存,浏览器会直接读取本地的缓存资源,当缓存过期之后会与服务器进行协商。

    深入理解浏览器缓存机制

    协商缓存

    协商缓存就是强制缓存失效后,浏览器携带缓存标识向服务器发起请求,由服务器根据缓存标识决定是否使用缓存的过程,主要有以下两种情况:

    •协商缓存生效,如果缓存和服务端资源的最新版本是一致的,那么就无需再次下载该资源,服务端直接返回304 Not Modified 状态码。

    •协商缓存失效,如果服务器发现浏览器中的缓存已经是旧版本了,那么服务器就会把最新资源的完整内容返回给浏览器,状态码就是 “200 ok”。

    协商缓存主要有四个头字段,它们两两组合配合使用,If-Modified-Since 和 Last-Modified一组,Etag 和 If-None-Match一组,当同时存在的时候会以Etag 和 If-None-Match为主。当命中协商缓存的时候,服务器会返回HTTP状态码304,让客户端直接从本地缓存里面读取文件。

    If-Modified-Since:请求头,资源最近修改时间,由浏览器告诉服务器。其实就是第一次访问服务端返回的Last-Modified的值。

    Last-Modified:响应头,资源最近修改时间,由服务器告诉浏览器。

    Etag:响应头,资源标识,由服务器告诉浏览器。

    If-None-Match:请求头,缓存资源标识,由浏览器告诉服务器。其实就是第一次访问服务端返回的Etag的值。

    If-Modified-Since 和 Last-Modified:

    当客户端首次请求资源的时候,服务器会把资源的最新修改时间 Last-Modified通过响应头发送给客户端,当再次发送请求时,客户端将服务器返回的修改时间放在请求头 If-Modified-Since发送给服务器,服务器再跟服务器上的对应资源进⾏⽐对,如果服务器的资源更新,那么返回最新的资源,返回200状态码,当服务器资源跟客户端请求的时间⼀致,证明客户端的资源是最新的,返回304状态码,就使用本地缓存,不需要再次发送请求。

    以上内容我们可以看出使用If-Modified-Since 和 Last-Modified带来的好处是:当缓存有效时服务器不会返回资源给到客户端,而是直接返回304状态码,让客户端直接从缓存中取资源,这样就大大节省了流量和带宽,并极大的减少了服务器的压力;但是也存在一些弊端:Last-Modified字段值只能精确到秒,假如在同一时间既修改文件又获取文件的话,此时客户端是获取不到最新文件的。

    Etag 和 If-None-Match:

    ETag的流程跟Last-Modified是类似的,区别就在于ETag是根据资源内容进⾏hash,⽣成⼀个信息摘要,只要资源内容有变化,这个摘要就会发⽣变化,通过这个摘要信息⽐对,即可确定客户端的缓存资源是否为最新,这⽐Last-Modified的精确度要更⾼。

    当客户端第一次请求服务器的时候,服务端会返回一个Etag响应头。客户端请求服务器的时候会带上If-None-Match请求头字段浏览器缓存机制有几种,该字段的值就是服务器返回的Etag的值。服务器接收到请求后会比较这两个值是否一样,一样就返回304,让客户端从缓存中读取,不一样就会返回新文件给客户端并更新Etag响应头字段的值。

    由此分析我们可以看出来,使用Etag 和 If-None-Match的好处:(除上述If-Modified-Since 和 Last-Modified的优点外)解决了一秒内修改并读取的问题。

    三.实际场景应用缓存策略

    频繁变动的资源

    Cache-Control: no-cache

    基于我们政务开放平台项目来说,考虑到用户接收资源等待时间,我们可以把频繁变动的资源通过优化缓存方案,给到用户更好的体验感。首先需要使用 Cache-Control:no-cache 使浏览器每次都请求服务器,然后配合 ETag 或者 Last-Modified 来验证资源是否有效。这样的做法虽然不能节省请求数量,但是能显著减少响应数据大小。

    2. 不常变化的资源

    Cache-Control: max-age=31536000

    基于我们政务开放平台项目来说,对于不经常需要变更的资源处理方式是,给它们的 Cache-Control 配置一个很大的 max-age=31536000 (一年),这样浏览器之后请求相同的 URL 会命中强制缓存。同时为了解决资源更新的问题,需要在文件名(或路径)中添加hash,版本号等动态字符,之后更改动态字符,从而达到更改引用 URL 的目的,让之前的强制缓存失效 (其实并未立即失效,只是不再使用了而已)。在线提供的类库 (如 jquery-3.3.1.min.js, lodash.min.js 等) 均采用这个模式。

    已上内容为本次文章探讨的浏览器缓存机制,详细介绍了强缓存和协商缓存的原理知识,希望本文章可以帮助更多的前端开发者对浏览器缓存机制有更透彻的认识并应用到自己的实际项目中。

    参考文章

    •《浅谈web缓存》

    •《前端面试手册》

    •《web缓存机制》

    •《彻底理解浏览器的缓存机制》

    •《前端面试之道》

    •《一文读懂前端缓存》

    版权声明

    本文仅代表作者观点。
    本文系作者授权发表,未经许可,不得转载。

    发表评论