кеш браузера
1. Зачем браузерам нужно кеширование
- Экономьте сетевые ресурсы
- Ускорить доступ к странице
2. Правила кэширования:
Все кэши основаны на наборе правил, чтобы решить, когда использовать копию в кэше для обслуживания, Два измерения свежести и значения проверки определяют, может ли браузер напрямую использовать копию в кеше или ему нужно перейти на исходный сервер для получения обновленной версии.
-
Свежесть (механизм истечения срока действия): то есть срок действия кэшированной копии. Кэшированная копия должна соответствовать следующим условиям, одного из которых достаточно, чтобы браузер считал ее действительной и достаточно актуальной:
- Содержит полную информацию о заголовке управления сроком действия (заголовок протокола HTTP) и все еще находится в пределах срока действия;
- Браузер уже использовал эту кешированную копию и проверил актуальность в течение сеанса;
-
Контрольное значение (механизм аутентификации): Когда сервер возвращает ресурс, иногда информация заголовка управления содержит тег сущности ресурса Etag (тег сущности). Его можно использовать в качестве флага проверки для процесса повторного запроса браузера. Если обнаруживается, что проверочный идентификатор не совпадает, это означает, что ресурс был изменен или срок его действия истек, и браузеру необходимо снова получить содержимое ресурса.
3.1 Принудительное кэширование
Cache-Control (1.1) и Expires (1.0)
let http = require('http');
let path = require('path');
let fs = require('fs');
let { promisify} = require('util');
let stat = promisify(fs.stat);//获取文件状态信息
// 静态服务器
let url = require('url'); // 专门用来处理url路径的
let server = http.createServer(async function (req,res) {
let { pathname,query} = url.parse(req.url,true); // 就是将query转化成对象
let readPath = path.join(__dirname, 'public', pathname);//文件绝对路径
try {
let statObj = await stat(readPath);
// 和客户端说 10m内走缓存
res.setHeader('Cache-Control','max-age=10'); //1.1
res.setHeader('Expires',new Date(Date.now()+10*1000).toGMTString()); //1.0
if (statObj.isDirectory()) {
let p = path.join(readPath, 'index.html');
await stat(p);//判断有没有这文件,如果读不到,就报错,就被catch到
// 如果当前目录下有html那么就返回这个文件
fs.createReadStream(p).pipe(res);
} else {
// 是文件 读取对应的文件直接返回即可
fs.createReadStream(readPath).pipe(res);
}
}catch(e){
res.statusCode = 404;
res.end(`Not found`);
}
}).listen(3000);
Недостатки: если наш индекс или содержимое css изменится в течение 10 секунд, оно все равно будет кэшироваться и не может быть обновлено вовремя.
Мы видим набор Cache-Control и ExpiresПереобновите браузер в течение 10 секунд, css ушел из кеша памяти3.2 Кэш сравнения (Last-Modified => if-modified-since)
let server = http.createServer(async function (req,res) {
let { pathname,query} = url.parse(req.url,true);
let readPath = path.join(__dirname, 'public', pathname);
try {
let statObj = await stat(readPath);
res.setHeader('Cache-Control','no-cache');
if (statObj.isDirectory()) {
let p = path.join(readPath, 'index.html');
let statObj = await stat(p);
res.setHeader('Last-Modified', statObj.ctime.toGMTString());//服务器设置文件最后修改时间
if (req.headers['if-modified-since'] === statObj.ctime.toGMTString()){//客户端请求的时间
res.statusCode = 304;
res.end();
return; // 走缓存
}
fs.createReadStream(p).pipe(res);
} else {
res.setHeader('Last-Modified', statObj.ctime.toGMTString());
if (req.headers['if-modified-since'] === statObj.ctime.toGMTString()) {
res.statusCode = 304;
res.end();
return; // 走缓存
}
fs.createReadStream(readPath).pipe(res);
}
}catch(e){
res.statusCode = 404;
res.end(`Not found`);
}
}).listen(3000);
Как показано на рисунке выше, если index.html не был изменен, вернуть 304 и перейти к кешу сравнения.Как показано выше, нет никаких изменений по сравнению с Last-Modified => If-modified-since.
3.3 Сравнить кеш (Etag => if-none-match)
let http = require('http');
let path = require('path');
let fs = require('fs');
let { promisify} = require('util');
let stat = promisify(fs.stat);
let url = require('url');
let crypto = require('crypto');
let server = http.createServer(async function (req,res) {
let { pathname,query} = url.parse(req.url,true);
let readPath = path.join(__dirname, 'public', pathname);
try {
let statObj = await stat(readPath);
res.setHeader('Cache-Control','no-cache');
if (statObj.isDirectory()) {
let p = path.join(readPath, 'index.html');
let statObj = await stat(p);
// 我要根据文件内容 生成一个md5的摘要 最耗性能 ,给实体加一个标签
let rs = fs.createReadStream(p);//读流
let md5 = crypto.createHash('md5'); // 不能写完相应体在写头
let arr = [];
rs.on('data',function (data) {
md5.update(data);//读一点加密一点
arr.push(data);//不能res.write(),下面setHeader还没完成
});
rs.on('end',function () {
let r = md5.digest('base64');
res.setHeader('Etag', r);//服务器设置Etag 和 客户端 if-none-match最对比
if (req.headers['if-none-match'] === r ){
res.statusCode = 304;
res.end();
return;
}
res.end(Buffer.concat(arr));
})
} else {
let rs = fs.createReadStream(readPath);
let md5 = crypto.createHash('md5'); // 不能写完相应体在写头
let arr = [];
rs.on('data', function (data) {
md5.update(data);
arr.push(data);
});
rs.on('end', function () {
let r = md5.digest('base64');
res.setHeader('Etag', r);
if (req.headers['if-none-match'] === r) {
res.statusCode = 304;
res.end();
return;
}
res.end(Buffer.concat(arr));
})
}
}catch(e){
res.statusCode = 404;
res.end(`Not found`);
}
}).listen(3000);
Как показано на рисунке выше, так как содержимое файла не изменилось, If-none-match совпадает с Etag, поэтому переходим к кешу
4 не идет кеш
- Запрос, кешируемый браузером, в основном является методом get, и сообщение редко использует кеш.
- Cache-Control:no-cache, pragma:no-cache, Cache-Control:max-age=0 установлены