聊聊浏览器的缓存策略

| 1.8k字 | 6分钟

为什么会有浏览器缓存策略

  1. 减少重复数据请求,避免通过网络再次加载资源,节省流量。
  2. 降低服务器的压力,提升网站性能。
  3. 加快客户端加载网页的速度, 提升用户体验。

何谓 【强缓存 vs 协商缓存】

强缓存:
若浏览器有缓存副本且未过期,则直接使用强缓存版本 cache-control: max-age

协商缓存:
即使本地有副本,也会携带缓存标识 If-modified-since/If-none-match到服务器验证副本有效性,
如果生效返回304,负责直接返回200

页面如何命中 强缓存和协商缓存?

浏览器缓存经典流程图-202204051756683.png

首先我们引用上面这幅经典的流程图进行说明:

  1. 浏览器在访问页面的时候,判断页面是否有被缓存过,如果有缓存,直接从缓存中读取数据进行访问即可。
  2. 如果没有缓存或缓存已过期,访问页面后,服务器会在Response Headers(在 Header 内的字段用于控制缓存机制)中返回一个Etag的标识符,第二次访问此页面时,会在Request Headers(在 Header 内的字段用于控制缓存机制)中携带对应的If-none-match,这个If-none-match和上面后端返回的Etag是一致的,因为值是Etag的值,这时候服务器会进行决策,如果返回200则请求响应,进行缓存协商。如果返回304,则从缓存读取。
  3. 步骤同2,如果Etag没有,则对应的查看是否有last-modified…..
  4. 如果Etaglast-modified都不存在,此时就只能服务器请求新的资源了…

网址链接实例讲解

我们以 juejin.cn 为例进行讲解

首次访问新链接后,页面会走协商缓存,服务端会在Response Headers中返回一个Etag的标识符。

首次输入juejin.cn链接后...202204051946208.png

再次(第二次)访问此链接后,Request Headers中携带对应的If-none-match,这个If-none-match和上面后端返回的Etag是一致的。此时携带此标识(If-none-match)到服务器端验证副本有效性,如果生效返回304,否则直接返回结果200

再次访问juejin.cn链接后,标识符匹配后...202204051953938.png

间隔一段时间后,缓存失效后,再次访问juejin.cn链接后, If-none-match和Etag不一致,此时缓存时效,此时会重新请求服务器获取新的资源

缓存失效后,再次访问juejin.cn链接后,标识符不一致时...202204052008883.png

画个图 总结一下(这里以Etag和If-none-match为例):
Last-Modified和If-modified-since流程同Etag和If-none-match一致

强缓存/协商缓存小结图-202204052020316.png

Etag是服务器响应请求时,返回当前资源文件的一个唯一标识(由服务器生成),If-none-match是客户端再次发起该请求时,携带上次请求返回的唯一标识Etag值,通过此字段值告诉服务器该资源上次请求返回的唯一标识值。服务器收到该请求后,发现该请求头中含有If-none-match,则会根据If-none-match的字段值与该资源在服务器的Etag值做对比,一致则返回304,代表资源无更新,继续使用缓存文件;不一致则重新返回资源文件,状态码为200。

If-modified-since则是客户端再次发起该请求时,携带上次请求返回的Last-Modified值,通过此字段值告诉服务器该资源上次请求返回的最后被修改时间。服务器收到该请求,发现请求头含有If-modified-since字段,则会根据If-modified-since的字段值与该资源在服务器的最后被修改时间做对比,若服务器的资源最后被修改时间大于If-modified-since的字段值,则重新返回资源,状态码为200;否则则返回304,代表资源无更新,可继续使用缓存文件


Etag/If-none-match 优先级高于 Last-Modified/If-modified-since

Last-Modified/Etag区别

相同点:

  • 两者都是用来处理协商缓存的

不同点:

  • last-modified 是根据文件修改时间,它的优点是计算量会更小,它就是根据文件的存储时间判断;但缺点就是非内容块改变(比如:只是meta标签发生了变化,但文件内容没有变化的情况下)系统也会判断这是一个新修改过的文件,这个时候就可能会造成缓存失效,还有就是last-modified单位是秒,如果一秒内修改了文件,last-modified是检测不到的。基于上面几种情况,所以选用了 Etag
  • Etag 是根据文件内容算出摘要,只有内容变化,Etag才会发生变化,那这样就可以保证,一定是文件内容发生了变化,缓存才会失效。缺点就是有一个摘要的计算(现在是依托在服务器的nginx中做摘要计算),可能会比较耗时

所以现在我们常用的比较好的缓存实践就是:

  • 文件名使用hash做路径标识 + 配合强缓存
  • 应用缓存 — App Cache and Manifest文件
  • localstorage — 做缓存
  • Service Worker — 实现自定义的网络请求逻辑

在HTTP/1.1中,Cache-Control是最重要的规则,主要用于控制网页缓存

Cache-Control:

  1. public:所有内容都将被缓存(客户端和代理服务器都可缓存)
  2. private:所有内容只有客户端可以缓存,Cache-Control的默认取值
  3. no-cache【重点】:告诉浏览器、缓存服务器,不管本地副本是否过期(last-modified)适应资源副本前一定要到原服务器进行副本有效性校验. 简单理解就是:指示浏览器每次使用url的缓存版本之前都必须与服务器重新验证
  4. no-store:所有内容都不会被缓存,即不使用强制缓存,也不使用协商缓存
  5. max-age=xxx (xxx is numeric):缓存内容将在xxx秒后失效
  6. must-revalidate:告诉浏览器、缓存服务器,本地副本过期前,可以使用本地副本;本地副本一旦过期,必须去源服务器进行有效性校验

注意:cache-control: max-age=0 暗示内容立即被认为是陈旧的(并且必须重新获取),这实际上与cache-control: no-cache 相同


注意点:

  1. 获取缓存检测缓存是否过期,如果没过期取缓存
  2. 优先从内存,其次硬盘,如果过期,则与服务器协商缓存是否仍然可用,如果不可用则获取,可用取缓存
  3. cache会在页面关闭时写入磁盘,下次打开同一站点时检测到未过期则会使用disk

如果文章有错误或者不严谨的地方,请务必给予指正,十分感谢 ~