说明:本篇文章以Chrome浏览器作为客户端,以Websrtom开启nodejs服务端,以请求图片资源示例做代码演示。
一、如何查看浏览器端的缓存
1、chrome://cache,如果出现如下界面,说明浏览器版本较高,不支持直接查看。请采用第二种方式。
2、使用chrome cache view <https://download.csdn.net/download/tdcqzd/10620842>工具
二、缓存控制
HTTP/1.1定义的 Cache-Control 头用来区分对缓存机制的支持情况, 请求头和响应头都支持这个属性。通过它提供的不同的值来定义缓存策略。
1、禁止进行缓存
缓存中不得存储任何关于客户端请求和服务端响应的内容。每次由客户端发起的请求都会下载完整的响应内容。
Cache-Control: no-store Cache-Control: no-cache, no-store, must-revalidate
2、强制确认缓存
如下头部定义,此方式下,每次有请求发出时,缓存会将此请求发到服务器(译者注:该请求应该会带有与本地缓存相关的验证字段),服务器端会验证请求中所描述的缓存是否过期,若未过期(注:实际就是返回304),则缓存才使用本地缓存副本。
Cache-Control: no-cache
3、私有缓存和公共缓存
“public”
指令表示该响应可以被任何中间人(译者注:比如中间代理、CDN等)缓存。若指定了”public”,则一些通常不被中间人缓存的页面(译者注:因为默认是private)(比如
带有HTTP验证信息(帐号密码)的页面 或 某些特定影响状态码的页面),将会被其缓存。
而 “private” 则表示该响应是专用于某单个用户的,中间人不能缓存此响应,该响应只能应用于浏览器私有缓存中。
Cache-Control: private Cache-Control: public
4、缓存过期机制
过期机制中,最重要的指令是
“max-age=”,表示资源能够被缓存(保持新鲜)的最大时间。相对Expires而言,max-age是距离请求发起的时间的秒数。针对应用中那些不会改变的文件,通常可以手动设置一定的时长以保证缓存有效,例如图片、css、js等静态资源。
详情看下文关于缓存有效性的内容。
Cache-Control: max-age=31536000
5、缓存验证确认
当使用了 “must-revalidate”
指令,那就意味着缓存在考虑使用一个陈旧的资源时,必须先验证它的状态,已过期的缓存将不被使用。详情看下文关于缓存校验的内容。
Cache-Control: must-revalidate
三、图片数据请求示例——不使用缓存
nodejs开启服务端,便于Chrome请求数据。
数据交互如下:
http://localhost:8080 <http://localhost:8080> :返回字符串
http://localhost:8080/img <http://localhost:8080/img>: 返回图片资源。
server.js
const http = require("http") const host = "127.0.0.1"; const port = 8080 const
server = http.createServer();const fs = require("fs") server.on("request",
(requstMsg, respone) => {if (requstMsg.url === "/") { respone.writeHead(200,
"OK", {"Content-Type": "text/html; charset=utf-8"}); respone.end("请求根路径"); }
else if (requstMsg.url === "/img") { const readStream = fs.createReadStream(
"../imgs/yxc.jpg") respone.writeHead(200,"OK",{ "Content-Type":"image/jpeg" })
readStream.pipe(respone); } }) server.listen(port, host, () => { console.log(
"服务启动了") })
说明:后续缓存代码处理,会以此代码为基础修改。
四、图片数据请求示例——使用强缓存
1、如何使用强缓存?
响应头添加Cache-Control
respone.writeHead(200,"OK",{ "Content-Type":"image/jpeg", "Cache-Control":
"max-age=5" })
Cache-Control 通用消息头被用于在http 请求和响应中通过指定指令来实现缓存机制。缓存指令是单向的,
这意味着在请求设置的指令,在响应中不一定包含相同的指令。
2、Cache-Control指令介绍
指令详解:
可缓存性指令
public
表明响应可以被任何对象(包括:发送请求的客户端,代理服务器,等等)缓存。
private
表明响应只能被单个用户缓存,不能作为共享缓存(即代理服务器不能缓存它),可以缓存响应内容。
no-cache
在释放缓存副本之前,强制高速缓存将请求提交给原始服务器进行验证。
only-if-cached
表明客户端只接受已缓存的响应,并且不要向原始服务器检查是否有更新的拷贝
缓存到期指令
max-age=<seconds>
设置缓存存储的最大周期,超过这个时间缓存被认为过期(单位秒)。与Expires相反,时间是相对于请求的时间。
s-maxage=<seconds>
覆盖max-age 或者 Expires 头,但是仅适用于共享缓存(比如各个代理),并且私有缓存中它被忽略。
max-stale[=<seconds>]
表明客户端愿意接收一个已经过期的资源。 可选的设置一个时间(单位秒),表示响应不能超过的过时时间。
min-fresh=<seconds>
表示客户端希望在指定的时间内获取最新的响应。
stale-while-revalidate=<seconds>
表明客户端愿意接受陈旧的响应,同时在后台异步检查新的响应。秒值指示客户愿意接受陈旧响应的时间长度。
stale-if-error=<seconds>
表示如果新的检查失败,则客户愿意接受陈旧的响应。秒数值表示客户在初始到期后愿意接受陈旧响应的时间。
重新验证和重新加载
must-revalidate
缓存必须在使用之前验证旧资源的状态,并且不可使用过期资源。
proxy-revalidate
与must-revalidate作用相同,但它仅适用于共享缓存(例如代理),并被私有缓存忽略。
immutable
表示响应正文不会随时间而改变。资源(如果未过期)在服务器上不发生改变,因此客户端不应发送重新验证请求头(例如
If-None-Match或If-Modified-Since)来检查更新,即使用户显式地刷新页面。在Firefox中,immutable只能被用在
https:// transactions.
其他指令
no-store
缓存不应存储有关客户端请求或服务器响应的任何内容。
no-transform
不得对资源进行转换或转变。Content-Encoding, Content-Range, Content-Type
等HTTP头不能由代理修改。例如,非透明代理可以对图像格式进行转换,以便节省缓存空间或者减少缓慢链路上的流量。no-transform指令不允许这样做。
2、 max-age 介绍
max-age单位是秒。当HTTP协议的返回头中包含Cache- Control字段时,浏览器会对当前的资源进行缓存。缓存的时效由max-age决定。在
max-age设定的时间内对当前资源发起访问后使用的都是浏览器内部的缓存!超过max-age设定的时间后浏览器会选择使用协商缓存。
no-cache&no-store区别
no-cache
使用缓存前必须先确认其有效性。
no-store
不缓存请求或响应的任何内容。
no-cache:会缓存资源到缓存中,但不使用缓存中资源,重新请求资源
no-store:不会缓存资源到缓存中,直接重新请求资源。
3、代码示例:
const http = require("http") const host = "127.0.0.1"; const port = 8080 const
server = http.createServer();const fs = require("fs") server.on("request",
(requstMsg, respone) => {if (requstMsg.url === "/") { respone.writeHead(200,
"OK", {"Content-Type": "text/html; charset=utf-8"}); respone.end("请求根路径"); }
else if (requstMsg.url === "/img") { const readStream = fs.createReadStream(
"../imgs/yxc.jpg") /* * 使用强缓存请求 * */ respone.writeHead(200,"OK",{ "Content-Type"
:"image/jpeg", "Cache-Control":"max-age=5" }) readStream.pipe(respone); } })
server.listen(port, host, () => { console.log("服务启动了") })
4、强缓存使用的问题
1)设置的强缓存时间失效后,再次请求同一张照片(即刷新操作),客户端会重新发送请求,向服务器请求图片。
但是既然请求的是同一张图片,缓存中又存在该图片,那么从缓存中获取图片不是
更好吗?如何解决?
可以使用协商缓存解决。
2)设置的强缓存时间较长比如100年,第一次请求图片(yxc.jpg)后,某段数据间图片资源已经修改(yys.jpg)。在强缓存有效期内重新发送请求,此时客户端会从缓存获取图片,获取的资源修改前的图片(yxc.jpg)。
但是此时,我想要的是修改后的图片(yys.jpg),如何解决?
可以使用协商缓存解决。
五、图片数据请求示例——使用协商缓存
1、协商缓存实现原理
通过使用实体首部字段last-modified(响应)和 请求首部字段if-modified-since监控JS文件的变化。
如果JS文件修改了,通过使用控件自动更新last-modified和if-modified-since值。
如果last-modified和if-modified-since值相等,则返回304状态码,由浏览器从缓存中获取资源;如果值不相等,就重新请求资源。
2、 last-modified & if-modified-since
实体首部字段last-modified(响应)& 请求首部字段if-modified-since。
首部字段last-modified指明资源最终修改的时间,一般来说,这个值就是资源被修改的时间。
首部字段if-modified-since:第一次请求资源时,资源在响应头中种入last-modified
字段,并随着响应体一起存到缓存中下一次需要再发送请求时,请求体中会将上一次修改时间(last-modified)种入if-modified-since
字段中带到服务端。若在if-modified-since字段值之后对应的资源都没有更新过,则返回304 Not Modified状态码
若在if-modified-since字段值之后对应的资源有过更新则希望服务器能重新处理。 则返回 成功 200 和失败 500
此处的客户端与服务端协商的缓存策略一般与cache-control一块使用。需要在cache-control失效后再走这种缓存策略
3、如何使用协商缓存?
响应头添加last-modified
respone.writeHead(200, "OK", { "Content-Type": "image/jpeg", "Cache-Control":
"max-age=5", "last-modified": data })
4、缓存策略中客户端做了什么?
首先应该明确,只有服务端是无法处理缓存的,那么客户端浏览器做了哪些功能?
1)更新if-modified-since;
客户端通过服务端响应,判断last-modified是否变化,并更新if-modified-since。
2)客户端通过判断响应头状态,决定是否从缓存中获取资源
如果响应头状态是304,客户端就会从缓存中获取资源。而不是重新请求资源。
5、缓存策略代码:
const http = require("http") const host = "127.0.0.1"; const port = 8080 const
server = http.createServer();const fs = require("fs") //data
:JS文件更新标识符,通常采用更新时间(由控件更新),监控js文件变化,如果js文件修改,data更新const data = "Wed, 22 Aug
2020 03:45:18 GMT" server.on("request", (requstMsg, respone) => { if
(requstMsg.url ==="/") { respone.writeHead(200, "OK", {"Content-Type":
"text/html; charset=utf-8"}); respone.end("请求根路径"); } else if (requstMsg.url ===
"/img") { /* * 使用协商缓存请求 * */ if (requstMsg.headers["if-modified-since"] ===
data) { respone.writeHead(304, "keep cache", { "Content-Type": "image/jpeg",
"Cache-Control": "max-age=5", "last-modified": data }) respone.end(); } else {
const readStream = fs.createReadStream("../imgs/yxc.jpg") respone.writeHead(200,
"OK", { "Content-Type": "image/jpeg", "Cache-Control": "max-age=5",
"last-modified": data }) readStream.pipe(respone); } } }) server.listen(port,
host, () => {console.log("服务启动了") })
6、如何告诉浏览器一直使用协商缓存?
当第二次请求资源时,使用协商策略,那么此后所有的请求都应该使用协商策略,而不使用强缓存策略。如何实现?即如何告诉浏览器一直使用协商缓存?
解决方案:修改Cache-Control
"Cache-Control": "max-age=1000",
修改为
"Cache-Control": "max-age=1000, no-cache",
即可。
因为强缓存优先级小于协商缓存,所以修改Cache-Control后即可告诉客户端一直使用协商缓存。
修改后代码示例:
const http = require("http") const host = "127.0.0.1"; const port = 8080 const
server = http.createServer();const fs = require("fs") //data
:更新时间(由控件更新),监控js文件变化,如果js文件修改,data更新const data = "Wed, 22 Aug 2020 03:45:18
GMT" server.on("request", (requstMsg, respone) => { if (requstMsg.url === "/")
{ respone.writeHead(200, "OK", {"Content-Type": "text/html; charset=utf-8"});
respone.end("请求根路径"); } else if (requstMsg.url === "/img") { /* * 使用强缓存+协商缓存请求
* 如果走到协商缓存,代表强缓存失效,以后一直使用协商缓存 * 告诉浏览器一直使用协商缓存?no-cache * 强缓存优先级小于协商缓存。 * */ if
(requstMsg.headers["if-modified-since"] === data) { respone.writeHead(304,
"keep cache", { "Content-Type": "image/jpeg", "Cache-Control":"no-cache",
"last-modified": data }) respone.end(); } else { const readStream =
fs.createReadStream("../imgs/yxc.jpg") respone.writeHead(200, "OK", {
"Content-Type": "image/jpeg", "Cache-Control": "max-age=10000, no-cache",
"last-modified": data }) readStream.pipe(respone); } } }) server.listen(port,
host, () => {console.log("服务启动了") })
7、协商缓存使用的问题即last-modified缺陷
1)某些服务端没有办法获取精确的修改时间,导致last-modified有问题
2)文件时间修改了,但文件内容却没有变
通俗来讲,last-modified的值就是JS文件更新标识符,如果JS文件变化了,last-modified的值就会动态更新。 它只认文件的最后修改时间。
可如果JS文件修改了但资源文件并没有修改或者值错了,按照当前的协商缓存策略,客户端应该是重新请求图片,而不是从缓存中获取。因此需要优化协商缓存策略.
六、图片数据请求示例——协商缓存优化
1、实现原理:
通过使用响应首部字段Etag & 请求首部字段if-None-Match监控JS文件资源的变化。
如果S文件资源修改了,通过使用控件自动更新etag 和if-none-match值。
如果etag 和if-none-match值相等,表示资源未修改,则返回304状态码,由浏览器从缓存中获取资源;
如果etag 和if-none-match值不相等,通过使用实体首部字段last-modified(响应)和 请求首部字段if-modified-since
监控JS文件的变化。
如果JS文件修改了,通过使用控件自动更新last-modified和if-modified-since值。
如果last-modified和if-modified-since值相等,则返回304状态码,由浏览器从缓存中获取资源;如果值不相等,就重新请求资源。
2、 Etag&if-None-Match
响应首部字段etag & 请求首部字段if-None-Match
响应首部字段etag:它可以告知客户端实体标识,它是一种可以将资源以字符串做唯一标识的方式,服务器会为每份资源分配对应的Etag值。另外当资源更新时,
etag的值也需要更新,这个唯一标识的生成没有规定统一的算法,由服务器自行决定
请求首部字段if-None-Match:机制和if-modified-since相似,当if-None-Match字段与etag
不一致时,就告知服务器该处理这请求
此处的客户端与服务端协商的缓存策略一般与cache-control一块使用。需要在cache-control失效后再走这种缓存策略。
3、代码示例
const http = require("http") const host = "127.0.0.1"; const port = 8080 const
server = http.createServer();const fs = require("fs") //data
:JS文件更新标识符,通常采用更新时间(由控件更新),监控js文件变化,如果js文件修改,data更新const data = "Wed, 22 Aug
2020 03:45:18 GMT" //etag
:JS文件资源更新标识符(由控件更新),监控js文件资源(图片资源)变化,如果js文件文件资源(图片资源)修改,etag更新const etag =
"12346"; server.on("request", (requstMsg, respone) => { if (requstMsg.url ===
"/") { respone.writeHead(200, "OK", {"Content-Type": "text/html; charset=utf-8"
}); respone.end("请求根路径"); } else if (requstMsg.url === "/img") { /* *
使用强缓存+协商缓存请求 * 如果走到协商缓存,代表强缓存失效,以后一直使用协商缓存 * 告诉浏览器一直使用协商缓存?no-cache *
强缓存优先级小于协商缓存。 * */if(requstMsg.headers["if-none-match"] === etag){
respone.writeHead(304,"keep cache",{ "Content-Type":"image/jpeg",
"Cache-Control":"no-cache", "last-modified":data, "Etag":etag }) //
告诉浏览器处理完毕,客户端忽略内容 respone.end(); }else if (requstMsg.headers["if-modified-since"
] === data) { respone.writeHead(304, "keep cache", { "Content-Type":
"image/jpeg", "Cache-Control":"no-cache", "last-modified": data, "Etag":etag })
respone.end(); }else { const readStream = fs.createReadStream("../imgs/yxc.jpg"
) respone.writeHead(200, "OK", { "Content-Type": "image/jpeg", "Cache-Control":
"max-age=10000, no-cache", "last-modified": data, "Etag":etag })
readStream.pipe(respone); } } }) server.listen(port, host, () => { console.log(
"服务启动了") })
七、Chrome缓存的一个注意点
当重新请求(刷新)一个URL时,RequestHeaders总是莫名多一个Cache-Control: max-age=0。相当于工具栏中勾选Disable
cahce.表示禁用缓存。
八、总结
热门工具 换一换