0%

http-browser-cache

Introduction

缓存是前端开发中非常重要的概念,它可以提高网站的性能,减少服务器的负担。本文主要介绍Http和浏览器缓存的相关知识。

不知道各位读者是否有这样的体验:

  1. 某个网站第一次打开的时候特别慢,再次打开的时候就快很多。
  2. 登录某个网站后,下次再访问的时候自动就是登录状态了。

其实以上现象都是缓存的功劳。

Http Cache

缓存相关的头部字段

Expires

Expires是一个HTTP响应头部字段,用于指定资源的过期时间。它的值是一个GMT格式的时间字符串。以下代码表示资源的过期时间是2024年11月16日14点10分23秒。

1
expires:Sat, 16 Nov 2024 14:10:23 GMT

Expires是HTTP/1.0的标准,它有一个问题,就是我们判断缓存过期时,浏览器会使用当前系统时间与Expires字段的时间进行比较。如果系统时间大于Expires字段的时间,那么缓存就会被认为过期了。但是当前系统时间是可以人为修改的,这就会导致缓存过期时间不准确。

Cache-Control

Cache-Control字段的取值有如下几种:

max-age

max-age的值是一个整数,单位是秒,表示缓存会在xxx秒后过期。以下代码表示资源会在3600秒后过期。

1
cache-control:max-age=3600

那么max-age中指定的3600秒参照的是什么时间呢?它参照的是服务器生成Response的时间,而不是浏览器收到Response的时间。那么服务器生成Response的时间在响应头中有吗?有的,就是Date字段。Date字段表示服务器生成Response的时间。所以缓存的的过期时间是Date字段的时间加上max-age的值。

ExpireTime = Date + max-age

注意:如果max-ageExpires同时存在,那么max-age的优先级更高,Expires会被忽略。

no-cache

注意:这个字段的意思不是不使用缓存!
这个字段的意思是可以使用缓存,但是使用前需要向服务器验证缓存是否过期。也就是协商缓存。

no-store

不使用缓存,每次都需要向服务器请求资源。

must-revalidate

在缓存过期前,直接使用缓存,但是过期后需要向服务器验证缓存是否过期。

Last-Modified

Last-Modified是一个HTTP响应头部字段,用于指定资源的最后修改时间。它的值是一个GMT格式的时间字符串。以下代码表示资源的最后修改时间是2024年11月16日14点10分23秒。

1
last-modified:Sat, 16 Nov 2024 14:10:23 GMT

当客户端再次请求资源时,会将上次请求时服务器返回的Last-Modified值放在请求头中的If-Modified-Since字段中,服务器会根据这个值判断资源是否发生了变化。如果资源没有发生变化,服务器会返回304 Not Modified状态码,表示资源没有发生变化,客户端可以使用缓存。

ETag

ETag是一个HTTP响应头部字段,用于指定资源的唯一标识。它的值是一个字符串。服务器会根据资源的内容生成一个唯一的字符串(注意,生成算法因服务器而异),然后将这个字符串作为ETag的值。当客户端再次请求资源时,会将上次请求时服务器返回的ETag值放在请求头中的If-None-Match字段中,服务器会根据这个值判断资源是否发生了变化。如果资源没有发生变化,服务器会返回304 Not Modified状态码,表示资源没有发生变化,客户端可以使用缓存。

ETag相比Last-Modified的优点

精确性:

  • ETag 可以提供比 Last-Modified 更精确的资源版本标识。Last-Modified 是基于时间戳的,而时间戳的精度通常是秒级,这意味着如果一个资源在一秒内发生变化多次,Last-Modified 就无法区分这些变化。
  • ETag 是一个可以由服务器自由定义的字符串,它可以唯一标识一个资源的特定版本,甚至可以包括内容的哈希值,从而确保即使是微小的变化也能被准确识别。

灵活性:

  • ETag 允许更灵活的缓存控制策略。它可以是强验证器(保证内容完全一致)或弱验证器(允许一定程度上的差异,如压缩、格式转换等),这取决于服务器如何生成 ETag。
  • 例如,当资源的内容没有变化,但是某些元数据发生了变化(比如编码方式),服务器可以选择更新 ETag 或者不更新,这取决于是否希望客户端重新获取资源。

避免时间戳问题:

  • 由于 Last-Modified 基于时间,它可能会遇到时钟不同步的问题。如果客户端和服务器之间的系统时钟存在较大偏差,那么基于 Last-Modified 的缓存验证可能会出错。
  • ETag 不依赖于时间戳,因此不受时钟同步问题的影响。

支持非文件资源:

  • 对于动态生成的内容(例如数据库查询结果),可能没有明确的最后修改时间,此时使用 ETag 更为合适。服务器可以根据内容的状态生成一个唯一的 ETag。

总结

  1. expiresmax-age控制的是强缓存。
  2. Last-ModifiedETag控制的是协商缓存。
  3. 浏览器请求时会先检查强缓存,然后再检查协商缓存。

一道面试题

如果一次Http请求返回的状态码是200,那么这个请求走缓存了吗?
答案:可能走,也可能没走。
解析:我们大多数人都知道状态码304表示资源没有改变,那么就使用本地缓存,这个属于协商缓存。还有一种是强缓存,就是直接使用本地缓存,不需要向服务器验证。这种情况下,状态码是200。

References

  1. https://developer.mozilla.org/en-US/docs/Web/HTTP/Caching
  2. https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control
  3. https://juejin.cn/book/6994678547826606095/section/6997029635766616077?scrollMenuIndex=1