浏览器灵魂拷问一:浏览器缓存

    科技2022-08-05  115

    浏览器缓存

    在web缓存中主要有前端:浏览器缓存,页面缓存; 后端:服务器缓存和数据库缓存。今天我们主要学习浏览器缓存。

    网络请求一般分为三个步骤:请求–>处理–>响应,当下我们初步了解下浏览器的请求和响应部分。

    缓存是我们性能优化中最重要的一环,也是经常使用的,接下来我们主要从强缓存中的expire是和Cache-control以及协商缓存以及缓存位置讲解。

    失效策略:

    强缓存

    浏览器中一般分为需要发送http请求和不需要发送的,首先是我们要检查强缓存,这个阶段不需要发送http请求。

    在http/1.0中使用的是Expire是,而在http/1.1中使用的是Cache-Control.

    强制缓存含义是:当客户端请求后会先访问缓存数据库看缓存是否存在,如果存在则直接返回,如果不存在则请求服务器,响应后在写入缓存。

    强缓存有点:直接减少请求数,是提升最大的缓存策略,如果优化网页性能则会优先选取。

    expires

    Expires 过期时间存在于服务器端返回的相应头中,告诉浏览器在这个过期时间间可以直接从缓存中获取数据,无需再次请求,如下:

    Expires: Wed, 22 Nov 2020 08:00:00 GMT

    表示资源在2002年11月22号八点过期,如果过期则需要重新向服务器端发送请求,但是由于服务器的时间和浏览器的时间可能不并不一致,那服务器返回的这个过期时间可能就是不准确的,因此1.1 中不在使用。

    Cache-control

    在http/1.1中关键字段:Cache-Control,这个字段与1.0不同在于没有采用具体的过期时间方式,而是采用过期时长来控制缓存,如果Expires和Cache-Control同时存在情况下优先使用Cache-Control,对应字段max-age,如下

    Cache-Control:max-age=3600 ## 表示响应返回后再3600s内可以使用缓存。

    当然Cache-Control还提供了除max-age属性外的其他属性如下 (可以混用):完整如MDN

    private: 这种情况只有浏览器能缓存,中间代理服务器不能缓存,默认值。 public:所有内容都可以被缓存(包括客户端和代理服务器,如cdn) no-cache: 跳过当前的强缓存(不要缓存),但实际上还是要求客户端缓存内容,只是是否使用这个内容由后续的对比做决定。发送HTTP请求,即直接进入协商缓存阶段 no-store:不进行任何缓存,所有内容均不走缓存,包括强制和对比(协商) must-revalidate: 如果超过了max-age的时间,浏览器必须向服务器发送请求,验证资源是否有效

    协商缓存

    强缓存失效后,浏览器在请求头中携带相应的缓存tag来向服务器发送请求,由服务器根据这个tag来决定是否使用缓存,这就是协商缓存 。

    流程如下: 浏览器先请求缓存数据库,返回一个缓存标识。之后浏览器拿这个标识和服务器通讯,如果缓存未失效,则返回HTTP状态码304表示继续使用,于是客户端继续使用缓存; 如果失效,则返回新的数据和缓存规则,浏览器响应数据后,在把规则写入缓存数据库。

    缓存tag分为两种:Last-Modified 和 ETag

    Last-Modified

    1)即最后修改时间,在浏览器首次给服务器发送请求后,服务器通过Last-ModifIed字段告知浏览器,浏览器将这个值和内容一起记录到缓存数据库中。 Last-Modified: Mon, 22 Nov 2020 08:00:00 GMT 2)下一次请求相同资源时候浏览器从自己缓存中找出这个缓存,浏览器会在请求头中将这个资源值写入到If-Modified-Since 字段中发送请求,这个值就是服务器传来最后修改时间。

    3)服务器拿到请求头中的If-Modified-Since字段后,会和Last-Modified字段值进行对。 a) 如果请求头中的这个值小于最后修改时间,说明是时候更新了。相应200状态码并返回新的资源,跟常规的HTTP请求响应的流程一样。 b) 如果相同返回304, 告诉浏览器直接用缓存。

    缺陷:

    如果资源更新速度以s为单位,该花奴才能是不能被用的,因为最低是单位是s

    如果文件是通过服务器动态生成的,那么该方法更新时间永远是生成的时间,尽管文件可能没有变化,所以起不到缓存作用。

    ETag

    如果都支持情况下, ETag的优先级会高于Last-Modified

    1)Etag是服务器根据当前文件的内容给文件生成唯一标识,只要里面内容有改动这个值就会变,服务器通过响应头将这个值给浏览器。

    2)浏览器接收到ETag的值,会在下次请求中将这个值存入If-None-Match字段携带。

    3)服务器收到If-None-Match后,会跟服务器上该资源的ETag进行对比: 3.1)如果两者不一样,说明要更新了,返回新的资源,更常规的HTTP请求响应流程一样。 3.2)如果返回304,告诉浏览器直接用缓存。

    两者对比

    在精确度上: ETag优于Last-Modified ,因为ETag能够准确感知资源的变化,而LM是一些特殊情况下并不能准确感知情况如: 编辑了文件,但是文件内容并没有改变,造成缓存失效LM感知时间单位是S,如果1S内改变很多次,这个时候LM并没有体现出修改。 性能上:Last-Modified优于ETag,Lm仅仅只是记录一个时间点,而ETag需要根据文件的具体内容生成哈希值。

    缓存位置分类(优先级高–>低)

    前面提到的强缓存或者协商缓存中服务器返回304时候,我们直接读取本地资源,那么这个时候我们就要知道这些资源存储到哪里?

    Service Worker

    Service Worker借鉴了Web Worker的思路,即让JS运行在主线程之外,由于它脱离了浏览器的窗体,因此无法直接访问DOM, 虽然如此但是它依然能帮助我们完成很多有用的功能,比如离线缓存、消息推送、网络代理等功能。 其中的离线缓存就是Service Worker Cache。

    相对于其他两个,serviceWorker 直接读取数据:Application->CacheStorage,且缓存永久有效,只有手动调用cache.delete(resource) 或者超量限制被浏览器清空。

    如果Service Worker没命中缓存,一般情况下会使用fetch() 方法继续获取资源。这个时候浏览器就去memory cache 或者disk cache 进行下一次找缓存的工作了。

    memory cache

    Memory Cache 指的是内存缓存,按常理先读内存后读硬盘。从效率上讲它是最快的,但是从存活时间来讲又是最短的,当渲染进程结束后,内存缓存也就不存在了。

    几乎所有的网络请求资源都会被浏览器自动加入memory cache中,常规下浏览器TAB关闭后该次浏览的memory cache 便失效了,而如果极端情况下一个页面缓存太大则会直接被浏览器失效,这里对资源缓存有两种:

    preloader:浏览器在解析js/css同时请求下一个资源,那么这些被preloader请求过来的资源被放入memory cache中,供之后解析执行使用preload:预加载资源也会放入到memory cache中

    注意:

    memory cache机制保证了一个页面中如果有两个相同的请求,都实际只会被请求最多一次。

    不过在匹配缓存时,除了匹配完全相同的URL之外,还会对比他们的类型,CORS中的域名规则等。因此一个作为脚本类型被缓存的资源是不能用在图片类型的请求中的,几遍他们src相同。

    从memory cache 获取缓存内容时候,浏览器忽略max-age=0和no-cache等头部配置,例如页面上存在几个相同的src图片,即便他们可能被设置为不缓存,但依然会从memorycache中读取,因为memory cache只是短期使用,生命周期只有一次浏览而已。

    如果不想任何东西进缓存,则设置no-store。

    disk cache (HTTP cache)

    即磁盘中的缓存,因为在磁盘上所有是持久缓存,而且它允许相同的资源在跨回话,甚至跨站点情况下使用,例如两个站点都使用了同一张图片,从存取效率上不如内存缓存,但是优势在于存储容量大且存储时间长。

    比较大的JS、CSS文件会直接被丢进磁盘,反之丢进内存。内存使用率比较高的时候,文件优先进入磁盘。

    disk cache 会严格按照HTTP请求头中各类字段来判断哪类资源可以缓存,哪些资源不可缓存,哪些资源仍然可用或者是过时需要新请求的; 当命中缓存之后,浏览器会从硬盘读取资源,虽然比起从内存读取慢了一些,但比网络请求更快,绝大部分缓存都来自diskcache。

    缺陷: 因为永久存储会增长内存,所以浏览器会自动清理,根据最老或者最长过时资源一个一个删除掉,每个浏览器算法不同体现差异不同。

    请求网络

    如果上述三个位置没有找到缓存,那么浏览器会正式发送网络请求,然后添加到缓存中

    根据Service Worker中的handler决定是否存入Cache Storage根据HTTP头部相关字段决定是否存入disk cache。memory cache保存一份资源的引用,以备下次。

    缓存小结

    当浏览器要请求资源时

    调用Service Worker的fetch响应事件查看memory cache查看disk cache 3.1 如果有强制缓存且未失效,则使用强制缓存,不请求服务器,返回状态码200。 3.2 如果有强制缓存但已失效,使用对比缓存,比较后确定304还是200.发送网络请求,等待网络响应。将响应内容存入disk cache中(考虑HTTP头部信息)将响应内容的引用存入memory cache(无视HTTP头信息)将响应内容存入Service Worker的Cache Storage(如果Service Worker脚本调用了cache.put() )
    Processed: 0.031, SQL: 8