聊聊web缓存那些事!

在说缓存那些事之前,必须对http常用的状态码有一些简单的了解:

  • 200 请求已成功,请求所希望的响应头或数据体将随此响应返回。出现此状态码是表示正常状态
  • 304 Not Modified 强制缓存
  • 当然还有很多其他的状态码,以上200 和 304是本篇文章重点讲到的。

为什么要缓存?

  1. 减少响应延迟:当服务器发现文件没有更新直接返回304状态码,告诉浏览器直接读缓存就好了,不需要传输资源文件;
  2. 减少网络带宽消耗:当副本被重用时会减低客户端的带宽消耗;客户可以节省带宽费用,控制带宽的需求的增长并更易于管理。
  3. 如果设置了Cache-Control,expires这样的之类,在有效时间之内不需要再次向服务器发送请求而是直接读取缓存(注意这样情况的状态码是200 也是强制缓存的一种

怎么缓存

其实就是通过设置http的头信息来进行设置各种缓存的

  1. Cache-Control
//HTTP 1.1 引入的
//Cache-Control: no-cache/max-age=600
let http = require('http')
let url = require('url')
let util = require('util')
let fs = require('mz/fs')
let stat = util.promisify(fs.stat);
let path = require('path');
let p = path.resolve(__dirname);
http.createServer(async function(req, res) {
    let {pathname} = url.parse(req.url);
    let realPath = path.join(p, pathname);
    console.log(realPath)
    try{
        let statObj = await fs.stat(realPath);
        console.log(statObj)
        res.setHeader('Cache-Control','max-age=10')  //强制缓存 10s内不需要再次请求服务器
        //res.setHeader('Cache-Control','no-cache')
        res.setHeader('Content-Type',require('mime').getType(realPath)+';charset=utf8')
       fs.createReadStream(realPath).pipe(res)
    }catch(e) {
        res.statusCode = 404;
        res.end('404')
    }
}).listen(3000)

我们请求一个本地的文件
// index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
    
    <img src="/agree.png"/>
</body>
</html>
//在浏览器打开localhost:3000/index.html
//可以看到状态码是200,这个200有两种情况一种是直接读取缓存的 
//1. (from memory cache) 缓存在内存当中;(from disk cache) 缓存在硬盘中
//2. 真的是从服务器中拉取过来的

复制代码
  1. expires
//Expires:Sun, 22 Jul 2018 02:43:42 GMT
//备注:如果Cache-Control和Expires同时存在,Cache-Control说了算
res.setHeader('Expires', new Date(Date.now() + 10*1000).toGMTString()) //强制缓存的另一种方式,另外强调一点:主网页只有对比缓存没有强制缓存
复制代码
  1. Last-Modified
//对比缓存 为了更加明显的看到对比缓存,我们将在以下的代码中都将强制缓存关闭
//res.setHeader('Cache-Control','no-cache')

//响应头设置了res.setHeader('Last-Modified',statObj.ctime.toGMTString())
//请求头就会带上req.headers['if-modified-since']

let http = require('http')
let url = require('url')
let util = require('util')
let fs = require('mz/fs')
let stat = util.promisify(fs.stat);
let path = require('path');
let p = path.resolve(__dirname);
http.createServer(async function(req, res) {
    let {pathname} = url.parse(req.url);
    let realPath = path.join(p, pathname);
    console.log(realPath)
    try{
        let statObj = await fs.stat(realPath);
        console.log(statObj)
        // res.setHeader('Cache-Control','max-age=10')  //强制缓存  10s内不需要再次请求服务器
        res.setHeader('Cache-Control','no-cache')
        res.setHeader('Content-Type',require('mime').getType(realPath)+';charset=utf8')
        res.setHeader('Expires', new Date(Date.now() + 10*1000).toGMTString()) //强制缓存 因为上面设置了no-cache,所以这里的设置其实无效
        let since = req.headers['if-modified-since'];
        if (since === statObj.ctime.toGMTString()) {
            res.statusCode = 304                      //服务器的缓存
            res.end();
        } else {
            res.setHeader('Last-Modified',statObj.ctime.toGMTString())
            fs.createReadStream(realPath).pipe(res)
        }
    }catch(e) {
        res.statusCode = 404;
        res.end('404')
    }
}).listen(3000)
//在浏览器打开localhost:3000/index.html  刷新看到就是304,我们返回状态码304,浏览器就乖乖地去读缓存中的文件了。
//我们稍微改动一下index.html就可以看到 200 
复制代码
  1. Etag
//对比缓存
//Etag内容的标识 
// 响应头设置了res.setHeader('Etag',statObj.size.toString());  这里设置的是文件大小
//请求头就会带上req.headers['if-none-match'];
//
let http = require('http');
let util = require('util');
let fs = require('fs');
let stat = util.promisify(fs.stat);
let url = require('url');
let path = require('path');
let p = path.resolve(__dirname);
// 比较内容 stat.size (不靠谱)
// 第一次请求Etag:内容的标识  
// 第二次在请求我的时候 if-none-match 
http.createServer(async function(req,res){
    let {pathname} = url.parse(req.url);
    let realPath = path.join(p,pathname);
    try{
        let statObj = await stat(realPath);
        console.log(realPath) 
        res.setHeader('Cache-Control','no-cache');
        let match = req.headers['if-none-match'];
        if(match){
            if(match === statObj.size.toString()){
                res.statusCode = 304;
                res.end();
            }else{
                res.setHeader('Etag',statObj.size.toString());
                fs.createReadStream(realPath).pipe(res);
            }
        }else{
            res.setHeader('Etag',statObj.size.toString());
            fs.createReadStream(realPath).pipe(res);
        }
       
    }catch(e){
        res.statusCode = 404;
        res.end(`not found`);
    }
}).listen(3000);

复制代码

两种缓存的区别

  1. 强制缓存
  • 设置强制缓存的方式就是 res.setHeader('Cache-Control','max-age=10')
  • res.setHeader('Expires', new Date(Date.now() + 10*1000).toGMTString())
  • 以上两种以第一种方式取决定作用
  1. 对比缓存
  • 通过时间对比 Last-Modified ---- if-modified-since
  • 通过标识对比 Etag ---- if-none-match

以上就是web缓存的部分内容,不足之处欢迎各位提出宝贵的意见或建议,也希望能帮助到你从中获得一些知识,谢谢大家的关注!

猜你喜欢

转载自juejin.im/post/5b1a7cd16fb9a01e7342d960