Вы понимаете кеширование браузера?

внешний интерфейс сервер браузер CSS

кеш браузера

1. Зачем браузерам нужно кеширование

  • Экономьте сетевые ресурсы
  • Ускорить доступ к странице

2. Правила кэширования:

Все кэши основаны на наборе правил, чтобы решить, когда использовать копию в кэше для обслуживания, Два измерения свежести и значения проверки определяют, может ли браузер напрямую использовать копию в кеше или ему нужно перейти на исходный сервер для получения обновленной версии.

  • Свежесть (механизм истечения срока действия): то есть срок действия кэшированной копии. Кэшированная копия должна соответствовать следующим условиям, одного из которых достаточно, чтобы браузер считал ее действительной и достаточно актуальной:

    1. Содержит полную информацию о заголовке управления сроком действия (заголовок протокола HTTP) и все еще находится в пределах срока действия;
    2. Браузер уже использовал эту кешированную копию и проверил актуальность в течение сеанса;
  • Контрольное значение (механизм аутентификации): Когда сервер возвращает ресурс, иногда информация заголовка управления содержит тег сущности ресурса 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 установлены