http强制缓存和协商缓存

141 阅读6分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第7天,点击查看活动详情

为什么需要http缓存

http缓存机制是web性能优化的重要手段,它可以通过复用获取过的资源,降低服务器压力,减少等待时间,节约网路流量。同时也提升了用户体验。

强制缓存

页面首次加载资源,一定会向服务器发送请求,服务器收到请求后,返回数据和缓存规则(缓存规则信息包含在响应header中,一般是服务端配置的),浏览器收到数据后根据缓存规则,将数据缓存在浏览器的缓存中。 当该资源再次需要请求时,浏览器会根据自己缓存记录,知道该url是否缓存过,如果缓存过,就直判断缓存是否失效,如果没有失效,就代表了命中缓存,此次请求无需向服务器发送请求,直接读取缓存中的数据即可, 缓存未命中情况 首先,当前一定不是第一次请求,浏览器缓存过当前的资源,经过缓存规则的校验,数据已经失效,或者被人为删除,这种情况就是缓存未命中,此时浏览器就会向服务器发送请求,服务器得到请求后,像第一处理该请求一样,返回数据和缓存规则,浏览器收到请求后,根据缓存规则记录,并且缓存数据,再有这个请求,就会走上面的缓存命中了, 强制缓存,规则一般有两个字段。Expires(过期时间),Cache-Control(缓存控制) Expires expires的值(格林威治时间)为服务器返回数据的过期时间,就是下次请求时,请求时间小于服务器返回的到期时间,直接使用缓存数据,否则视为缓存失效,重新向服务器发送请求。 expires 是 http1.0实现的,测试时,发现chrome 好像忽略了这个配置, 现在浏览器默认使用的是http1.1,因为过期时间是服务器生成的,由于服务器和客户端时间可能存在误差,这样就会导致缓存命中不按套路命中 Cache-Control 是重要的缓存规则,常见的取值有privat\pulic\no-cache\max-age\no-store,默认为private private:客户端可以缓存 public:客户端和代理服务器都可以缓存 max-age=xxx 换窜的内容在xxx秒后失效 no-cache:不使用强制缓存,需要使用协商缓存来验证缓存数据 no-store: 所有内容都不会缓存

image.png

max-age 值为一个数字,单位为秒 当然要自己准备一个图,什么格式都行,路径对应就可以./public/1.gif demo node 启动后访问http://localhost:8080/

const http = require('http');
const fs = require('fs');
const path = require('path');
const readFile = (...reset) => {
    let url = path.join(__dirname, './public', ...reset);
    return new Promise((resolve, reject) => {
        fs.readFile(url, (err, data) => {
            if (err) {
                reject(err)
            }
            resolve(data)
        })
    })
}

const indexHtml = `

<body>
    <div>我是tests</div>
    <img src="./1.gif">
</body>
`
const server = http.createServer(async (req, res) => {
    const {
        url
    } = req;
    let data
    switch (url) {
        case "/":
            res.writeHead(200, {
                'content-Type': "text/html;charset:=utf-8"
            })
            // 正常是读一个文件,但是为了demo简单,就把html接口直接用字符串替代了
            // data = await readFile('./index.html');

            data = indexHtml
            break;
        case "/1.gif":
            res.writeHead(200, {
                // 初次请求后,3秒内再次访问该资源,无需访问服务器,读取缓存即可
                "Cache-Control":'max-age=3' 
            })
            data = await readFile(url);
            break;
    }
    res.end(data);
})
let port = 8080;
server.listen(port, () => {
    console.log(`服务启动在http://localhost:${port}`)
})

命中缓存效果

image.png 强制缓存一般是给一些长时间不会改变的资源,就是能保证服务器端和混存里的东西等价,但是对于更新比较频繁的资源要慎用,例如天气预报信息,今天获取的信息是昨天的天气,用户肯定会骂人的

协商缓存

协商缓存不管缓存是否会命中,都会向服务器发送请求。 浏览器第一次请求数据,服务器端会将数据和缓存标识一起返回给客户端,客户端端将二者保存到浏览器缓存数据库中,当再次发起这个请求时,客户端将备份标识发送给服务器,服务器根据缓存标识进行判断,判断如果缓存命中,就返回304,告诉客户端,数据没失效,可以复用,浏览器器屁颠屁颠的直接将缓存数据做为返回数据了, 如果缓存未命中,服务器返回新的数据和新的缓存标识,浏览器更新缓存中的数据和标识,留着下次请求使用 协商缓存 http1.0中header中标识有last-Modified / if-Modified-Since last-Modified 服务器在响应请求时,告诉浏览器资源最后修改时间 if-Modifined-Since 再次发送请求时,通过此字端作为当前缓存数据的标识发送到服务器,服务器根据这个字段,和被请求的资源的最新的最后修改时间进行比对,如果资源最后修改时间和请求带来的if-Modified-Since不同,说名资源改动过,返回最新数据,状态码为200,如果最后修改时间相同,则命中缓存,响应304状态码,无需响应数据,浏览器自然懂的,继续使用缓存中的数据。 Etag /if-None-Match (http1.1 优先级高于last-Modified/if-Modified-Since) Etag 服务器响应请求时,告诉浏览器当前资源在服务器的唯一标识(生成规则由服务器决定,一般是content hash ,但是数据量大时,可以采用优化方法获取hash) if-None-Match 再次请求资源时,带着这个字端问服务器,用缓存还是给新的数据,服务器根据这个值,得到结果。命中缓存后,返回304状态码,缓存未命中时,返回新的数据和新的标识,浏览器和服务的默契不谋而合,狼狈为奸 配置方法 协商缓存和强缓存配合使用给个短时间的强缓存,之后使用协商缓存。 强缓存设置, "Cache-Control": 'max-age=0,no-cache' "last-Modified": lastModified

如下 增加./public/index.css

body{
    background-color:red;
}
...
const getFileUpdatedDate = (path) => {
    const stats = fs.statSync(path)
    return new Date(stats.mtime).toUTCString()
}
...

  case "/1.gif":
            res.writeHead(200, {
                // 初次请求后,3秒内再次访问该资源,无需访问服务器,读取缓存即可
                "Cache-Control": 'max-age=3'
            })
            data = await readFile(url);
            break;
  case "/index.css":
            const lastModified = getFileUpdatedDate("./public/index.css");
            const ifModifiedSince = req.headers['if-modified-since'];
            console.log(lastModified, ifModifiedSince)
            if (ifModifiedSince && ifModifiedSince == lastModified) {
                res.writeHead(304);
            } else {
                // 协商缓存和强缓存配合使用
                // 给个短时间的强缓存,
                res.writeHead(200, {
                    'content-Type': "text/css;charset=utf-8",
                    "Cache-Control": 'max-age=0,no-cache',
                    "last-Modified": lastModified
                })
                data = await readFile(url);
            }
            break;
  ...

无论怎么刷新,css都是304,但是改变一下css并且保存,就会返回最新的css了