Open yuanyuanbyte opened 2 years ago
本系列的主题是浏览器,每期讲解一个技术要点。如果你还不了解各系列内容,文末点击查看全部文章,点我跳转到文末。
如果觉得本系列不错,欢迎 Star,你的支持是我创作分享的最大动力。
缓存:保存资源副本并在下次请求时直接使用该副本。
HTTP 缓存分为 2 种,一种是强缓存,另一种是协商缓存。
缓存的主要作用是可以加快资源获取速度,提升用户体验,减少网络传输,缓解服务端的压力。。
强缓存就是不需要发送请求到服务端,直接读取浏览器本地缓存,在 Chrome 的 Network 中显示的 HTTP 状态码是 200 。
在 Chrome 中,强缓存又分为 Disk Cache (存放在硬盘中)和 Memory Cache (存放在内存中),存放的位置是由浏览器控制的。是否强缓存由 Expires、Cache-Control 和 Pragma 3 个 Header 属性共同来控制。
Expires 的值是一个 HTTP 日期,在浏览器发起请求时,会根据系统时间和 Expires 的值进行比较,如果系统时间超过了 Expires 的值,缓存失效。由于和系统时间进行比较,所以当系统时间和服务器时间不一致的时候,会有缓存有效期不准的问题。Expires 的优先级在三个 Header 属性中是最低的。
Expires
Cache-Control 是 HTTP/1.1 中新增的属性,在请求头和响应头中都可以使用,常用的属性值如有:
Cache-Control
Pragma 只有一个属性值,就是 no-cache ,效果和 Cache-Control 中的 no-cache 一致,不使用强缓存,需要与服务器验证缓存是否新鲜,在 3 个头部属性中的优先级最高。
Pragma
no-cache
本地通过 express 起一个服务来验证强缓存的 3 个属性,代码如下:
express
const express = require('express'); const app = express(); var options = { etag: false, // 禁用协商缓存 lastModified: false, // 禁用协商缓存 setHeaders: (res, path, stat) => { res.set('Cache-Control', 'max-age=10'); // 强缓存超时时间为10秒 }, }; app.use(express.static((__dirname + '/public'), options)); app.listen(3000);
第一次加载,页面会向服务器请求数据,并在 Response Header 中添加 Cache-Control ,过期时间为 10 秒。
Response Header
第二次加载,Date 头属性未更新,可以看到浏览器直接使用了强缓存,实际没有发送请求。
过了 10 秒的超时时间之后,再次请求资源:
当 Pragma 和 Cache-Control 同时存在的时候,Pragma 的优先级高于 Cache-Control。
协商缓存是一种服务端的缓存策略,即通过服务端来判断某件请求是否可以命中缓存。
服务端判断客户端的资源,是否和服务端资源一样,如果一致则返回 304 ,反之返回 200 和最新的资源。
当浏览器的强缓存失效的时候或者请求头中设置了不走强缓存,并且在请求头中设置了If-Modified-Since 或者 If-None-Match 的时候,会将这两个属性值到服务端去验证是否命中协商缓存,如果命中了协商缓存,会返回 304 状态,加载浏览器缓存,并且响应头会设置 Last-Modified 或者 ETag 属性。
If-Modified-Since
If-None-Match
Last-Modified
ETag
ETag / If-None-Match 的值是一串 hash 码,代表的是一个资源的标识符,当服务端的文件变化的时候,它的 hash码会随之改变,通过请求头中的 If-None-Match 和当前文件的 hash 值进行比较,如果相等则表示命中协商缓存。ETag 又有强弱校验之分,如果 hash 码是以 "W/" 开头的一串字符串,说明此时协商缓存的校验是弱校验的,只有服务器上的文件差异(根据 ETag 计算方式来决定)达到能够触发 hash 值后缀变化的时候,才会真正地请求资源,否则返回 304 并加载浏览器缓存。
Last-Modified / If-Modified-Since 的值代表的是文件的最后修改时间,第一次请求服务端会把资源的最后修改时间放到 Last-Modified 响应头中,第二次发起请求的时候,请求头会带上上一次响应头中的 Last-Modified 的时间,并放到 If-Modified-Since 请求头属性中,服务端根据文件最后一次修改时间和 If-Modified-Since 的值进行比较,如果相等,返回 304 ,并加载浏览器缓存。
本地通过 express 起一个服务来验证协商缓存,代码如下:
const express = require('express'); const app = express(); var options = { etag: true, // 开启协商缓存 lastModified: true, // 开启协商缓存 setHeaders: (res, path, stat) => { res.set({ 'Cache-Control': 'max-age=00', // 浏览器不走强缓存 'Pragma': 'no-cache', // 浏览器不走强缓存 }); }, }; app.use(express.static((__dirname + '/public'), options)); app.listen(3001);
第一次请求资源:
第二次请求资源,服务端根据请求头中的 If-Modified-Since 和 If-None-Match 验证文件是否修改。
ETag / If-None-Match 的出现主要解决了 Last-Modified / If-Modified-Since 所解决不了的问题
Etag
即:
如果看完前面的文章后对HTTP 强缓存和协商缓存 还不是特别理解,可以继续看下面的文章,或许能帮到你; 如果前面的文章已经帮你理解了 HTTP 强缓存和协商缓存,则可以略过下面的文章。
如果看完前面的文章后对HTTP 强缓存和协商缓存 还不是特别理解,可以继续看下面的文章,或许能帮到你;
HTTP 强缓存和协商缓存
如果前面的文章已经帮你理解了 HTTP 强缓存和协商缓存,则可以略过下面的文章。
强缓存
先看第一个图:
从上图可以看到,当初次请求时,浏览器会向服务器发起请求,服务器接收到浏览器的请求后,返回资源并返回一个 Cache-Control 给客户端,该 Cache-Control 一般设置缓存的最大过期时间。
接下来看第二个图:
从上图中可以看到,此时浏览器已经接收到 cache-control 的值,那么这个时候浏览器再次发送请求时,它会先检查它的 cache-control 是否过期,如果没有过期则直接从本地缓存中拉取资源,返回到客户端,而无需再经过服务器。
cache-control
接下来看第三个图:
强制缓存有过期时间,那么就意味着总有一天缓存会失效。那么假设某一天,客户端的 cache-control 失效了,那么它就没办法从本地缓存中拉取资源。于是它会像第一张图一样,重新向服务器发起请求,之后服务器会再次返回资源和 cache-control 的值。 以上就是强制缓存的全过程。
协商缓存
先来看第一张图:
在上图中,表明了协商缓存的全过程。首先,如果客户端是第一次向服务器发出请求,则服务器返回资源和相对应的资源标识给浏览器。该资源标识就是对当前所返回资源的一种唯一标识,可以是Etag或者是Last-Modified,这两个字段将在图例结束后展开讲解。
之后如果浏览器再次发送请求时,浏览器就会带上这个资源标识。此时,服务端就会通过这个资源标识,可以判断出浏览器的资源跟服务端此时的资源是否一致,如果一致,则返回304,即表示Not Found 资源未修改。如果判断结果为不一致,则返回200,并返回资源以及新的资源标识。至此就结束了协商缓存的过程。
接下来看第二张图:
假设此时我们的协商缓存用 Last-Modified 来判断。当浏览器第一次发送请求时,服务器返回资源并返回一个 Last-Modified 的值给浏览器。这个 Last-Modified 的值给到浏览器之后,浏览器会通过 If-Modified-Since 的字段来保存 Last-Modified 的值,且 If-Modified-Since 保存在请求头当中。
之后当浏览器再次发送请求时,请求头会带着 If-Modified-Since 的值去找服务器,服务器此刻就会匹配浏览器发过来的 If-Modified-Since 是否和自己最后一次修改的 Last-Modified 的值相等。如果相等,则返回 304 ,表示资源未被修改;如果不相等,则返回200,并返回资源和新的 Last-Modified 的值。
接下来看第三张图:
假设此时我们的协商缓存用 Etag 来判断。当浏览器第一次发送请求时,服务器返回资源并返回一个 Etag 的值给浏览器。这个 Etag 的值给到浏览器之后,浏览器会通过 If-None-Match 的字段来保存 Etag 的值,且 If-None-Match 保存在请求头当中。
之后当浏览器再次发送请求时,请求头会带着 If-None-Match 的值去找服务器,服务器此刻就会匹配浏览器发过来的 If-None-Match 是否和自己最后一次修改的 Etag 的值相等。如果相等,则返回 304 ,表示资源未被修改;如果不相等,则返回 200 ,并返回资源和新的 Etag 的值。
查看原文
查看全部文章
各系列文章汇总:https://github.com/yuanyuanbyte/Blog
我是圆圆,一名深耕于前端开发的攻城狮。
本系列的主题是浏览器,每期讲解一个技术要点。如果你还不了解各系列内容,文末点击查看全部文章,点我跳转到文末。
如果觉得本系列不错,欢迎 Star,你的支持是我创作分享的最大动力。
分析 304 状态码过程,图解 HTTP 强缓存和协商缓存
什么是缓存
缓存:保存资源副本并在下次请求时直接使用该副本。
HTTP 缓存分为 2 种,一种是强缓存,另一种是协商缓存。
为什么需要缓存?
缓存的主要作用是可以加快资源获取速度,提升用户体验,减少网络传输,缓解服务端的压力。。
强缓存
强缓存就是不需要发送请求到服务端,直接读取浏览器本地缓存,在 Chrome 的 Network 中显示的 HTTP 状态码是 200 。
在 Chrome 中,强缓存又分为 Disk Cache (存放在硬盘中)和 Memory Cache (存放在内存中),存放的位置是由浏览器控制的。是否强缓存由 Expires、Cache-Control 和 Pragma 3 个 Header 属性共同来控制。
Expires
Expires
的值是一个 HTTP 日期,在浏览器发起请求时,会根据系统时间和Expires
的值进行比较,如果系统时间超过了Expires
的值,缓存失效。由于和系统时间进行比较,所以当系统时间和服务器时间不一致的时候,会有缓存有效期不准的问题。Expires
的优先级在三个 Header 属性中是最低的。Cache-Control
Cache-Control
是 HTTP/1.1 中新增的属性,在请求头和响应头中都可以使用,常用的属性值如有:Pragma
Pragma
只有一个属性值,就是no-cache
,效果和Cache-Control
中的no-cache
一致,不使用强缓存,需要与服务器验证缓存是否新鲜,在 3 个头部属性中的优先级最高。本地通过
express
起一个服务来验证强缓存的 3 个属性,代码如下:第一次加载,页面会向服务器请求数据,并在
Response Header
中添加Cache-Control
,过期时间为 10 秒。第二次加载,Date 头属性未更新,可以看到浏览器直接使用了强缓存,实际没有发送请求。
过了 10 秒的超时时间之后,再次请求资源:
当
Pragma
和Cache-Control
同时存在的时候,Pragma
的优先级高于Cache-Control
。协商缓存
协商缓存是一种服务端的缓存策略,即通过服务端来判断某件请求是否可以命中缓存。
服务端判断客户端的资源,是否和服务端资源一样,如果一致则返回 304 ,反之返回 200 和最新的资源。
当浏览器的强缓存失效的时候或者请求头中设置了不走强缓存,并且在请求头中设置了
If-Modified-Since
或者If-None-Match
的时候,会将这两个属性值到服务端去验证是否命中协商缓存,如果命中了协商缓存,会返回 304 状态,加载浏览器缓存,并且响应头会设置Last-Modified
或者ETag
属性。ETag/If-None-Match
ETag
/If-None-Match
的值是一串 hash 码,代表的是一个资源的标识符,当服务端的文件变化的时候,它的 hash码会随之改变,通过请求头中的If-None-Match
和当前文件的 hash 值进行比较,如果相等则表示命中协商缓存。ETag
又有强弱校验之分,如果 hash 码是以 "W/" 开头的一串字符串,说明此时协商缓存的校验是弱校验的,只有服务器上的文件差异(根据 ETag 计算方式来决定)达到能够触发 hash 值后缀变化的时候,才会真正地请求资源,否则返回 304 并加载浏览器缓存。Last-Modified/If-Modified-Since
Last-Modified
/If-Modified-Since
的值代表的是文件的最后修改时间,第一次请求服务端会把资源的最后修改时间放到Last-Modified
响应头中,第二次发起请求的时候,请求头会带上上一次响应头中的Last-Modified
的时间,并放到If-Modified-Since
请求头属性中,服务端根据文件最后一次修改时间和If-Modified-Since
的值进行比较,如果相等,返回 304 ,并加载浏览器缓存。本地通过 express 起一个服务来验证协商缓存,代码如下:
第一次请求资源:
第二次请求资源,服务端根据请求头中的
If-Modified-Since
和If-None-Match
验证文件是否修改。ETag
/If-None-Match
的出现主要解决了Last-Modified
/If-Modified-Since
所解决不了的问题Last-Modified
和Etag
的值时,会优先使用Etag
;Last-Modified
只能精确到秒级;Etag
更精确即:
Last-Modified
/If-Modified-Since
会错误地返回 304Last-Modified
/If-Modified-Since
会错误地返回200 并返回资源强缓存
先看第一个图:
从上图可以看到,当初次请求时,浏览器会向服务器发起请求,服务器接收到浏览器的请求后,返回资源并返回一个
Cache-Control
给客户端,该Cache-Control
一般设置缓存的最大过期时间。接下来看第二个图:
从上图中可以看到,此时浏览器已经接收到
cache-control
的值,那么这个时候浏览器再次发送请求时,它会先检查它的cache-control
是否过期,如果没有过期则直接从本地缓存中拉取资源,返回到客户端,而无需再经过服务器。接下来看第三个图:
强制缓存有过期时间,那么就意味着总有一天缓存会失效。那么假设某一天,客户端的 cache-control 失效了,那么它就没办法从本地缓存中拉取资源。于是它会像第一张图一样,重新向服务器发起请求,之后服务器会再次返回资源和
cache-control
的值。 以上就是强制缓存的全过程。协商缓存
先来看第一张图:
在上图中,表明了协商缓存的全过程。首先,如果客户端是第一次向服务器发出请求,则服务器返回资源和相对应的资源标识给浏览器。该资源标识就是对当前所返回资源的一种唯一标识,可以是
Etag
或者是Last-Modified
,这两个字段将在图例结束后展开讲解。之后如果浏览器再次发送请求时,浏览器就会带上这个资源标识。此时,服务端就会通过这个资源标识,可以判断出浏览器的资源跟服务端此时的资源是否一致,如果一致,则返回304,即表示Not Found 资源未修改。如果判断结果为不一致,则返回200,并返回资源以及新的资源标识。至此就结束了协商缓存的过程。
接下来看第二张图:
假设此时我们的协商缓存用
Last-Modified
来判断。当浏览器第一次发送请求时,服务器返回资源并返回一个Last-Modified
的值给浏览器。这个Last-Modified
的值给到浏览器之后,浏览器会通过If-Modified-Since
的字段来保存Last-Modified
的值,且If-Modified-Since
保存在请求头当中。之后当浏览器再次发送请求时,请求头会带着
If-Modified-Since
的值去找服务器,服务器此刻就会匹配浏览器发过来的If-Modified-Since
是否和自己最后一次修改的Last-Modified
的值相等。如果相等,则返回 304 ,表示资源未被修改;如果不相等,则返回200,并返回资源和新的 Last-Modified 的值。接下来看第三张图:
假设此时我们的协商缓存用
Etag
来判断。当浏览器第一次发送请求时,服务器返回资源并返回一个Etag
的值给浏览器。这个Etag
的值给到浏览器之后,浏览器会通过If-None-Match
的字段来保存Etag
的值,且If-None-Match
保存在请求头当中。之后当浏览器再次发送请求时,请求头会带着
If-None-Match
的值去找服务器,服务器此刻就会匹配浏览器发过来的If-None-Match
是否和自己最后一次修改的Etag
的值相等。如果相等,则返回 304 ,表示资源未被修改;如果不相等,则返回 200 ,并返回资源和新的Etag
的值。参考
查看原文
查看全部文章
博文系列目录
交流
各系列文章汇总:https://github.com/yuanyuanbyte/Blog
我是圆圆,一名深耕于前端开发的攻城狮。