笔者曾在掘金上浏览技术文章,来学习http cache的知识。讲到的最多的固然是强缓存和协商缓存。关于各个字段的含义,咱们能够找到这张图: javascript
多数文章会告诉咱们Response Header里的Cache-Control各个取值,表明着不一样的含义。但不多有文章说起到Request Header里的Cache-Control。有一天发现:css
这是chrome刷新中,选择硬性从新加载,浏览器请求一个静态js文件的请求头。当时并不知道和响应头里的Cache-Control有啥区别。曾一度认为请求头里的Cache-Control是无效的。直到某一天,本身用nodejs实现了一次缓存设置。html
这里简单归纳下顺序前端
一个典型的截图以下:html5
Initiator理解为发起请求的对象,有如下值:java
Size的值理解为请求内容的大小,Time理解为请求消耗的时间:node
须要注意的是,memory cache和disk cache虽然没有发起实际的请求,但都会有200的状态码,以下图:ajax
右键点击刷新按钮会出现三个选项,如图:chrome
这种方式会从强缓存开始判断,是用户浏览网页中最经常使用的方式。json
这种方式会跳过强缓存,直接从协商缓存开始判断。
但须要注意的是Initiator值为Other的内容才会走协商缓存(一般只有一个,是html内容)。其余的内容,由于是从html里引入的(如script,link,img等),或者从script文件动态引入的。他们的Initiator一般是一个html文件,或者script文件,这些资源仍是会依照本身的规则,从强缓存开始判断;
这种方式会在Request Header里添加Cache-Control:max-age=0,这是浏览器本身的行为
这种方式,全部的资源(不论Initiator的值),都会跳过缓存判断,发起真实的请求,从服务端拿资源。但本地的缓存资源(如disk里的缓存)并无删除。 这种方式会在Request Header里添加Cache-Control:no-cache和Pragma: no-cache,也是浏览器本身的行为
这种方式,至关于先删除缓存(如disk里的缓存),再执行硬性从新加载
从访问页面的4种方式里,能够看出。在点刷新按钮、或者硬性从新加载等时,由于浏览器本身的行为,会在Request Header里加入对应的Cache-Control值。
须要弄清楚的是: 请求头里的Cache-Control影响的是当前这一次请求,响应头里的Cache-Control是告诉浏览器这样存储,下次依照这样来。影响的是下一次请求。 通常以上边的方式1(如url回车)访问时,默认是按照上次给的规则来。但浏览器在真正的发起此次请求时,能够有本身的行为,好比硬性从新加载,不走缓存
这个值表示,这个请求按照协商缓存的规则走,必定会发出真实的请求。这里和响应头里的max-age=0有不一样
这个值通常会附带Pragma: no-cache,是为了兼容http1.0。表示此次请求不会读缓存资源,即使缓存没有过时,或者资源并无修改,这样的请求不会有返回304的可能。这一点和响应头里的Cache-Control:no-cache是有区别的。
Request Header里Cache-Control只有这两个值可取,设置其余的值无效,好比设置Cache-Control:no-store是没有用的,这一点要和响应头里的Cache-Control区分开。
Request Header里的Cache-Control只有在浏览器刷新,硬性从新加载。这两种浏览器本身的行为中会被添加。 若是是一个常规的,设置了协商缓存(响应头里Cache-Control:no-cache),和不缓存(响应头里Cache-Control:no-cache)的请求,它在正常的,经过上文方式1访问时,是不会在请求头里添加Cache-Control值的。
单页面应用中,
在前边的介绍里,请求头里Cache-Control:max-age=0和Cache-Control:no-cache都是浏览器本身的行为,由于加载这个静态资源,如html、js、image等,用户是没法设置响应的Request Header的。
开发者可否本身设置html等静态资源的请求头呢,目前没有发现这类方法。值得一提的是html里的<meta>标签,在旧版本里或许能够设置响应的请求头如:
<meta http-equiv="cache-control" content="max-age=180" />
<meta http-equiv="cache-control" content="no-cache" />
<meta http-equiv="expires" content="0" />
<meta http-equiv="expires" content="Tue, 01 Jan 1980 1:00:00 GMT" />
<meta http-equiv="pragma" content="no-cache" />
复制代码
但这些方法在html5以后,是不会生效的。
有没有能设置请求头的请求呢,咱们会想到js发起的ajax请求,这个请求的Initiator通常是一个js文件。咱们能够经过xhr.setRequestHeader()
设置一些请求头,好比Cache-Control的值。另外,浏览器对js设置请求头的功能是有限制的,好比host
、cookie
、user-agent
这些是没法被js修改的。
通过笔者的测试
node环境运行的http.js的内容:
const http = require('http'); //引入http模块
const fs = require('fs'); //引入文件模块
const TIME = [60, 120];//第一个时间是Etag改变的时间,第二个max-age过时时间
const MAP = new Map([
[0, '123456789'],
[1, '987654321'],
]); //生成两个可选的Etag值
let FLAG = false; //状态,是否修改
const timer = setTimeout(() => {
FLAG = !FLAG; //必定时间后,修改状态
}, TIME[0] * 1000);
const server = http.createServer((req, res) => {
console.log(req.url);
//浏览器默认还会请求/favicon.ico这个ico资源,因此须要对url作监听限制
if(['/', '/get1'].includes(req.url)) {
res.setHeader('Cache-Control', `public, max-age=${TIME[1]}`);
//设置reponse header的Cache-Control,失效时间
console.log(req.headers['if-none-match'], '请求头里的Etag');
const nowEtag = MAP.get(FLAG ? 1 : 0); //当前的Etag值
if (req.headers['if-none-match'] === nowEtag) {
console.log(123,867);
//检查request header里的if-none-match和当前的Etag是否一致
//简单处理
res.statusCode = 304;
res.end();
} else {
res.setHeader('Etag', nowEtag); //设置协商缓存的Etag值
if(req.url === '/'){
const nowHtml = FLAG ? 'tempNew.html' : 'temp.html'; //当前应该渲染的页面
fs.createReadStream(nowHtml).pipe(res); //指定返回temp.html文件
}else if(req.url === '/get1'){
res.end(JSON.stringify({
"a": Date.now(),
"b": "qwer"
}))
}
}
}
})
server.listen(3333, '127.0.0.1', () => {
console.log(`Server running at http://127.0.0.1:3333/`);
})
复制代码
node中处理一个post请求,在这里作一个备份,之后学习会用到
if (req.url === '/post1') {
if (req.method == 'POST') {
let postData = ''
req.on('data', chunk => {
postData += chunk.tostring()
})
req.on('end', () => {
res.end(JSON.stringify({
"a": Date.now(),
"b": "qwer"
}))
})
}
}
复制代码
temp.html的内容:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<META HTTP-EQUIV="Pragma" CONTENT="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<title>test page</title>
</head>
<body>
<div>这是一个指定的首页</div>
<ul>
<li><button id="btn">GET</button></li>
<li><button id="btn1">GET no-cache</button></li>
<li><button id="btn2">GET max-age=0</button></li>
<li><button id="btn3">GET no-store</button></li>
</ul>
<script type="text/javascript">
window.onload = function() {
const GET = ty => {
const options = { method: 'GET', };
ty && (options['headers'] = new Headers({ 'Cache-Control': ty }));
return fetch('/get1', options);
//使用fetch主要是简洁,XMLHttpRequest要写太多代码
}
document.addEventListener('click', function(e) {
if(e.target.id === 'btn'){
//no Cache-Control
GET()
.then(res => {
console.log(res.json());
})
}else if (e.target.id === 'btn1') {
//no-cache
GET('no-cache')
.then(res => {
console.log(res.json());
})
}else if(e.target.id === 'btn2') {
//max-age=0
GET('max-age=0')
.then(res => {
console.log(res.json());
})
}else if(e.target.id === 'btn3') {
//no-store
GET('no-store')
.then(res => {
console.log(res.json());
})
}
})
}
</script>
</body>
</html>
复制代码
启动服务后,能够分别作下列两种操做:
这两组操做效果是不同的,可验证这两种刷新的区别。 另外,点no-store对应的GET的btn,效果是和普通的btn一致,由于在请求头里设置Cache-Control:no-store是无效的。请求头里的Cache-Control:no-cache已经能够作到此次请求不走缓存了。
node.js方便了前端开发调试问题,在没有后端的状况下,进行mock数据,缓存设置等,确实很方便。