霖呆呆你来讲说浏览器缓存吧

霖呆呆你来讲说浏览器缓存吧

都知道浏览器缓存和HTTP知识对咱们前端的重要性, 可是一直都没有对它进行很好的总结. 并且近几天流感🦠闹的挺严重的, 霖呆呆今年也是没有去拜年了, 窝在家里, 码码字吧.css

也是看了几篇比较好的关于浏览器缓存的文章, 有所收获, 因此想在这里整理总结给你们, 就算之后面试问到了关于这方面的内容也能够轻松应对😊.前端

不少时候, 面试官不会精确的问你某个知识点, 而是抛出一个有指向性可是又能够很发散的问题.web

好比: “嗯...那你说说浏览器缓存吧!”.面试

这时候考验的就是你对浏览器知识的广度与深度了.算法

当接到这样一个问题的时候, 你得冷静下来, 回想一下均可以从哪几个方面说? 各个大的方面又能够分为几个小的点说呢?浏览器

接下来, 让我来为你们拆分一下, 咱们能够从如下几个方面来回答这个问题:缓存

  • 缓存的类型 (强缓存or协商缓存)
  • 缓存位置 (Service Worker、Memory Cache...)
  • 缓存过程分析
  • 缓存策略的实际场景应用

缓存的类型

首先从缓存的类型上来讲, 能够分为两种: 强缓存协商缓存.安全

强缓存是不须要发送HTTP请求的, 而协商缓存须要.服务器

也就是在发送HTTP请求以前, 浏览器会先检查一下强缓存, 若是命中直接使用,不然就进入下一步。网络

强缓存

浏览器检查强缓存的方式主要是判断这两个字段:

  • HTTP/1.0时期使用的是 Expires;
  • HTTP/1.1使用的是** Cache-Control**

(expires中文意思有效期, cache-control中文意思缓存管理)

Expires

Expires字面意思表示的是有效期, 那么很好理解, 它表示的就是一个具体的时间.

例如🌰:

Expires: Wed, Nov 11 2020 08:00:00 GMT
复制代码

表示的就是这个资源在2020年11月11日8点过时, 到了过时时间了就得向服务端发请求了.

颇有意思的是, 如果设置了Expires, 可是服务器的时间与浏览器的时间不一致的时候(好比你手动修改了本地的时间), 那么就可能会形成缓存失效, 所以这种方式强缓存方式并非很准确, 它也所以在HTTP/1.1中被摒弃了.

Cache-Control

摒弃了Expires以后, HTTP/1.1采用了Cache-Control这个重要的规则. 它设置的是一个具体的过时时长, 其中的一个属性是max-age.

例如🌰:

Cache-Control: max-age=300
复制代码

表示的是这个资源在响应以后的300s内过时, 也就是5分钟以内再次获取这个资源会直接使用缓存.

Cache-Control不只仅有max-age 这一个属性, 其实它有不少的用法, 你甚至能够采用组合的方式:

Cache-Control: public, max-age=300
复制代码

上面👆用法的意思是响应能够被任何对象(客户端, 代理服务器等)缓存, 且过时时长为5分钟.

(由于一个请求经历的不只仅是客户端(浏览器)和目标服务器, 它中间有可能会通过不一样的代理服务器)

下面来例举一些经常使用的指令:

public: 客户端和代理服务器均可以缓存. 响应能够被中间任何的一个节点缓存, 好比一个请求要经历 Browser -> proxy1 -> proxy2 -> Server, 中间的代理(proxy)能够缓存资源. 下次再请求同一资源的时候, 浏览器就会直接到proxy1中拿缓存的东西而没必要向proxy2拿.

private: 这个是Cache-Control默认的取值, 只有客户端能够缓存, 中间节点不容许缓存. 在 Browser -> proxy1 -> proxy2 -> Server 这个过程当中, 代理(proxy)不会缓存任何数据, 当Browser再次请求时, proxy会把Server返回的数据发送给Brower, 作好请求转发, 而不是给本身缓存的数据.

no-cache: 表示不进行强缓存验证, 而是用协商缓存来验证.

no-store: 全部内容都不会被缓存, 不进行强缓存, 也不进行协商缓存.

max-age: 表示在多久以后过时, 好比max-age=300表示在300s后缓存内容失效.

s-max-age: 它的做用和max-age很像, 不过max-age 用于普通缓存, 而s-max-age用于代理缓存, 且s-max-age的优先级更高.

max-stale: 能容忍的最大过时时间。max-stale指令表示客户端愿意接收一个已通过期了的响应。

min-fresh:可以容忍的最小新鲜度。min-fresh表示客户端不肯意接受新鲜度很少于当前的age加上min-fresh设定的时间之和的响应。

基于上面👆的这些指令, 咱们能够将它们进行组合, 达到多个目的, 不一样的效果.

有一张来自《浪里行舟-深刻理解浏览器的缓存机制》中的图表述的很是好:

http1.png
http1.png

ExpiresCache-control的对比

  • Expires产于 HTTP/1.0, Cache-control产于 HTTP/1.1;
  • Expires设置的是一个具体的时间, Cache-control 能够设置具体时常还有其它的属性;
  • 二者同时存在, Cache-control的优先级更高;
  • 在不支持 HTTP/1.1的环境下, Expires就会发挥做用, 因此先阶段的存在是为了作一些兼容的处理.

协商缓存

在上面👆咱们已经介绍了强缓存, 它是不须要发送HTTP请求的, 如果强缓存失效, 则会进入协商缓存.

协商缓存归纳来讲就是浏览器会携带缓存标识(tag)向服务器发送请求, 服务器会根据缓存标识(tag)来决定是否使用缓存.

因此对于服务器的返回结果会有这两种状况:

  • 协商缓存生效, 返回304和Not Modified(空的响应体)
  • 协商缓存失效, 返回200和请求结果

而刚刚提到的这个缓存标识(tag)也是有两种.

分为**Last-Modified** 和 ETag.

Last-Modified 和 If-Modified-Since

从字面意思上咱们能够看出, Last-Modified表示的是资源的最后修改时间, 所以其中一种协商缓存判断的就是最后修改时间.

那它具体是怎样实现的呢🤔️?

其实使用Last-Modified进行协商缓存会通过如下几步:

  1. 浏览器第一次向服务器请求这个资源
  2. 服务器在返回这个资源的时候, 在 response header中添加 Last-Modifiedheader, 值为该资源在服务器上最后的修改时间
  3. 浏览器接收到后缓存文件和这个 header
  4. 当下次浏览器再次请求这个资源的时候, 检测到有 Last-Modified这个 header, 就会在请求头中添加 If-Modified-Since这个 header, 该值就是 Last-Modified
  5. 服务器再次接收到该资源的请求, 则根据 If-Modified-Since与服务器中的这个资源的最后修改时间作对比
  6. 对比结果相同则返回 304和一个空的响应体, 告诉浏览器从本身(浏览器)的缓存中拿
  7. 对比结果不一样( If-Modified-Since < 服务器资源最后修改时间), 则表示资源被修改了, 则返回200和最新的资源文件(固然还包括最新的 Last-Modefied)
http2.png
http2.png

ETag 与 If-None-Match

ETag其实与Last-Modefied的原理差很少, 不过它不是根据资源的最后修改时间来判断的, 而是经过一个惟一的标识😊.

在浏览器请求服务器资源的时候, 服务器会根据当前文件的内容, 给文件生成一个惟一的标识, 如果文件发生了改变, 则这个标识就会改变.

服务器会将这个标识ETag放到响应体的header中与请求的资源一块儿返回给浏览器, 而浏览器一样也会缓存文件与这个header.

在下一次再次加载该资源时, 浏览器会将刚刚缓存的ETag放到请求体头部(request header)的If-None-Match里发送给服务器.

一样的服务器接收到了以后与该资源自身的ETag作对比, 若是一致, 则表示该资源未发生改变, 则直接返回304知会客户端直接使用本地缓存便可. 如果不一致, 则返回200和最新的资源文件(固然还包括最新的ETag)

以下图:

http3.png
http3.png

二者对比

在进行对比以前, 咱们先来看看二者都有什么优缺点呢🤔️?

首先对于Last-Modified:

  • 如果本地打开了缓存文件, 并无进行修改, 也仍是会改变最后修改时间, 致使缓存失败;
  • 因为 Last-Modified是以秒来计时的, 如果某个文件在一秒内被修改了不少次, 那么这时候的 Last-Modified 并无体现出修改了.

而后对于ETag:

  • 性能上的不足,只要文件发生改变, ETag就会发生改变. ETag须要服务器经过算法来计算出一个hash值.

总结, 因此对于两种协商缓存:

  • 准确度上 ETag更强;
  • 性能上 Last-Modified更好;
  • 二者都支持的话, ETag优先级更高.

缓存位置

在上面👆咱们已经介绍完了缓存的类型😄, 可是以前也提到过了, 如果命中了强缓存或者服务器返回了304以后, 要浏览器从缓存中过去资源, 那这些缓存具体是存储在哪里呢?

从优先级上来讲分为如下四种:

  • Service Worker
  • Memory Cache
  • Disk Cache
  • Push Cache

Service Worker

Service Worker是运行在浏览器背后的独立线程, 也就是说它脱离了浏览器的窗体, 没法直接访问DOM.功能上主要是能实现: 离线缓存消息推送网络代理等.好比离线缓存就是Service Worker Cache.

简单来讲, 它有如下几个特色:

  • 借鉴了 Web Worker是思路
  • 使用 Service Worker会涉及到请求拦截, 因此须要用 HTTPS协议来保证安全, 传输协议必须是 HTTPS
  • 与浏览器其它内建的缓存机制不一样, 它可让咱们自由的控制缓存哪些文件、如何匹配读取缓存, 且缓存是持续性的
  • Service Worker同时也是 PWA的重要实现机制

Memory Cache

从命名上来讲, Memory Cache就是内存中的缓存, 存储的主要是当前页面已经抓取到的资源, 好比页面上已经下载的样式脚本图片等.

Memory Cache的特色:

  • 读取效率快, 但是缓存持续时间短, 会随着进程的释放而释放(一旦关闭 Tab页面, 就被释放了, 还有可能在没关闭以前, 排在前面的缓存就失效了, 例如一个页面的缓存占用了超级多的内存)
  • 几乎全部的请求资源都能进入 memory Cache, 细分来讲主要分为 preloaderpreload这两块.
  • 在从 memory Cache读取缓存时, 浏览器会忽视 Cache-Control中的一些 max-age、no-cache等头部配置, 除非设置了 no-store这个头部配置.

preloader

上面👆提到的preloader是页面优化的常见手段之一, 它的做用主要是用于在浏览器打开一个网页的时候,可以一边解析执行js/css, 一边去请求下一个资源, 而这些被 preloader 请求来的的资源就会被放入 memory Cache 中,供以后的解析执行操做使用。

preload

preloadpreloader仅两个字母之差, 它能显式指定预加载的资源, 这些资源也会被放进memory Cache中, 例如<link rel="preload">

Disk Cache

Disk Cache, 也叫作HTTP Cache, 是存储在硬盘上的缓存, 因此它是持久存储, 是实际存在于文件系统中的.

从存储效率上说, 它比内存缓存慢, 可是优点在于存储容量更大, 且存储时长更长.

在全部浏览器缓存中, Disk Cache是覆盖面最大的. 它会根据前面咱们提到的HTTP header中的缓存字段来判断哪些资源须要缓存, 哪些资源不须要请求而直接使用, 哪些已通过期了须要从新请求获取.

如果命中了缓存以后, 浏览器会从硬盘中直接读取资源, 虽然没有从内存中读取的快, 可是倒是比网络缓存快.

前面提到的强缓存和协商缓存也是属于Disk Cache, 它们最终都存储在硬盘里.

Memory CacheDisk Cache二者的对比:

  • 比较大的 JS、CSS文件会被丢硬盘中存储, 反之则存储在内存中
  • 当前系统内存使用率比较高的时候,文件优先进入磁盘

Push Cache

Push Cache(推送缓存), 它是浏览器缓存的最后一段防线, 当以上三种缓存都没有命中的时候, 它才会被使用.

我所知道的, 它只会在会话(Session)中存在, 一旦会话结束它就会被释放, 而且缓存时间也很短暂, 在Chrome浏览器中只有5分钟.

另外因为它是 HTTP/2 中的内容, 所以在国内不是很普及, 这里贴上一个比较好的总结:

  • 全部的资源都能被推送,而且可以被缓存,可是 Edge 和 Safari 浏览器支持相对比较差

  • 能够推送 no-cache 和 no-store 的资源

  • 一旦链接被关闭,Push Cache 就被释放

  • 多个页面可使用同一个HTTP/2的链接,也就可使用同一个Push Cache。这主要仍是依赖浏览器的实现而定,出于对性能的考虑,有的浏览器会对相同域名但不一样的tab标签使用同一个HTTP链接。

  • Push Cache 中的缓存只能被使用一次

  • 浏览器能够拒绝接受已经存在的资源推送

  • 你能够给其余域名推送资源

原文连接:https://www.jianshu.com/p/54cc04190252

缓存过程分析

上面👆已经向你们介绍了缓存类型已经缓存的位置, 那么浏览器具体的一个缓存行径是怎样的呢?

从浏览器发起HTTP请求到得到请求结果, 能够分为如下几个过程:

  1. 浏览器第一次发起 HTTP请求, 在浏览器缓存中没有发现请求的缓存结果和缓存标识
  2. 所以向服务器发起 HTTP请求, 得到该请求的结果还有缓存规则(也就是 Last-Modified 或者 ETag)
  3. 浏览器把响应内容存入 Disk Cache, 把响应内容的引用存入 Memory Cache
  4. 把响应内容存入 Service WorkerCache Storage (若是 Service Worker 的脚本调用了 cache.put())

下一次请求相同资源的时候:

  1. 调用Service Workerfetch事件响应

  2. 查看memory Cache

  3. 查看disk Cache. 这里细分为:

    • 有强缓存且未失效, 则使用强缓存, 不请求服务器, 返回的状态码都是200
    • 有强缓存且已失效, 使用协商缓存判断, 是返回304仍是200(读取缓存仍是从新获取)

缓存策略的实际场景应用

说了这么多缓存策略, 那么在实际使用上来讲, 咱们通常是怎样使用它的呢?

不常变化的资源

对于不常变化的资源:

Cache-Control: max-age=31536000
复制代码

一般是给Cache-Control设置成一个很大的值, (31536000, 一年). 这个也很好理解, 不常变化的资源, 直接让它使用缓存就是了.

可是有时候为了解决更新的问题, 咱们须要在文件名中添加上hash, 版本号等动态字段, 这样就达到了更改引用URL 的目的.

常变化的资源

常常变化的资源, 咱们进行如下配置:

Cache-Control: no-cache
复制代码

设置成以上配置, 使得浏览器每次都请求服务器, 而后配合ETag或者Last-Modified来验证资源是否有效.

后语

浏览器缓存的内容其实还有不少能够说的, 霖呆呆这里主要是总结了一些面试时常问到的, 你能够转化成本身的言语来回答面试官.

最近流感🦠的事情闹的挺严重的呀, 小伙们出门必定要带好口罩😷, 作好防御措施...

参考文章:

《神三元-(1.6w字)浏览器与前端性能灵魂之问,请问你能接得住几个?(上)》

《浪里行舟-深刻理解浏览器的缓存机制》

小蘑菇哥哥-一文读懂前端缓存

知识无价, 支持原创

相关文章
相关标签/搜索