http 缓存
概述
http 缓存分为强缓存和协商缓存。命中强缓存不需要客户端就不需要向服务器发送请求,直接使用本地缓存(Cache-Control/Expires);协商缓存客户端需要先携带协商缓存的首部(Last-Modified/If-None-Match) 到服务器进行验证,如果缓存有效则服务器会返回 304 状态码而且没有响应体,客户端直使用本地缓存,如果响应失效则返回最新的资源和相关的缓存头
强缓存
不需要客户端就不需要向服务器发送请求,直接使用本地缓存
对于强缓存的资源,可以看到返回的状态码是 200,并且会显示 from memory cache/from disk cache,强缓存是通过 Expires 或者是 Cache-Control 实现的,Cache-Control 的优先级高于 Expires
Expires
它的值是一个 GMT 的绝对时间,在该时间之前缓存都是有效的,不需要发送请求。
- 缺点:依赖于客户端本地的时间,存在客户端时间和服务器不一致或者本地时间被修改的问题,导致缓存不可用
Cache-Control
Cache-Control: max-age=60
单位秒,相对时间,相对于响应的资源被创建的时间而言在 60s 之内缓存都是有效的。优先级更高
Cache-Control 的其他值
值 | 含义 |
---|---|
public | 响应的内容可以被缓存在任何地方(客户端、CDN、代理服务器)服务端参数 |
private | 响应只能被缓存在客户端 服务端参数 |
max-age | 单位秒,响应在该时间范围内都是有效的 服务端参数 |
no-cache | 不是表示响应不能被缓存,响应可以被缓存,只是每次都需要去服务器验证响应是否有效 服务端参数 |
no-store | 响应不能被缓存,每次都需要去服务器获取最新的资源 服务端参数 |
max-stale | 单位秒,相对时间,允许接收过期的响应,但是过期的时间不能大于我们设置的这个时间 客户端参数 |
max-fresh | 单位秒,相对时间,响应必须要在该时间范围都是有效的 客户端参数 |
s-maxage | 单位秒,相对时间,会覆盖 max-age,但是只对代理服务器生效。 就是用于表示 cache 服务器上(比如 cache CDN,缓存代理服务器)的缓存的有效时间的,并只对 public 缓存有效 服务端参数 |
must-revalidate | 必须校验缓存中的资源是有效的才会被使用 |
协商缓存
协商缓存指的是在强缓存失效之后,浏览器携带协商缓存标识向服务器发送请求,验证请求是否有效。有效的话返回状态码 304 且没有响应体客户端直接使用本地缓存;无效的话则返回最新资源和缓存首部(状态码 200)。
Last-Modified & If-Modified-Since/If-UnmodifiedSince
Last-Modified 的值是我们请求的资源在服务器上的最后修改时间,是一个 GMT 的绝对时间。下次客户端请求这个资源的时候就会包含 If-Modified-Since 首部(值就是第一次请求时 Last-Modified 的值)判断是否和服务器上该资源的 Last-Modified 是否一致(大于)不一致则表明缓存失效。
缺点:
只能精确到秒,但是有些文件可能会在 1 秒内被修改多次,此时就会导致客户端使用的资源仍然是旧的
文件可能并没有被修改,但是 Last-Modified 却改变了,此时就会导致客户端无法使用缓存
Etag & If-None-Match/If-Match
Etag 是服务器根据某个资源内容生成的唯一标识。
优先级高于 Last-Modified
缓存机制总结
强缓存优先于协商缓存,强缓存失效的情况下才会使用协商缓存。协商缓存如果缓存失效的话服务器会返回最新的资源和相关的缓存头,资源会被客户端或者第三方缓存。
❗️ 没有设置缓存是浏览器的处理方式
浏览器会采用一个启发式的算法,通常会取响应头中的 Date 减去 Last-Modified 值的 10% 作为缓存时间。
缓存时间 = (Response Date - Last Modified) / 10
用户行为对缓存的影响
强缓存和协商缓存都生效:地址栏回车、链接跳转、新开窗口
强缓存失效,协商缓存有效:普通刷新(F5)
浏览器会在请求头里加一个“Cache-Control: max-age=0” 强行发起请求,它可以配合 ETag 和 Last-Modified 使用,如果本地缓存还在,且服务器返回 304 ,依然可以使用本地缓存。
- 强缓存和协商缓存都失效:强制刷新(Ctrl+F5)
发送的请求的请求头中均携带 Cache-Control: no-cache(为了兼容,还携带了 Pragma: no-cache),服务器直接返回 200 和最新的资源
缓存的弊端
用户访问了我们的页面,并且缓存了相关的资源,但是在这之后我们迭代了一些功能并且上线了这些功能,那么此时在用户不刷新页面的情况下是访问不到我们最新的功能的。
解决方式
- 通过第三方的打包工具(Webpack)通过一些配置可以为我们每次打包后的文件名加一些 hash 值,这样用户在我们上线完之后再访问网站时,请求的文件名发生变化,所以就会去服务器请求新的资源而不会使用本地缓存
webpack.config.js
module.exports = {
entry: "./src/index.js",
output: {
path: path.resolve(__dirname, "dist"),
filename: "[name].bundle.[hash].js"
}
};
- (不推荐)手动加版本号 ❌
缓存到底存在哪里
按缓存位置分类我们可以分为 memory cache、disk cache,我们可以在 Chrome 的开发者工具中,Network -> Size 一列看到一个请求最终的处理方式:如果是大小 (多少 K, 多少 M 等) 就表示是网络请求,否则会列出 from memory cache、from disk cache 就表示命中了缓存。
memory cache
是内存中的缓存,(与之相对 disk cache 就是硬盘上的缓存)。按照操作系统的常理:先读内存,再读硬盘。关闭 tab 也或者浏览器,缓存就会消失
disk cache
disk cache 也叫 HTTP cache,顾名思义是存储在硬盘上的缓存,因此它是持久存储的,是实际存在于文件系统中的。而且它允许相同的资源在跨会话,甚至跨站点的情况下使用,例如两个站点都使用了同一张图片。