likes
comments
collection
share

Web开发中的强缓存和协商缓存策略(一)

作者站长头像
站长
· 阅读数 14

Web开发中的强缓存和协商缓存策略(一)

一. 概念

前端缓存是Web开发中非常重要的一部分,它可以提高网站的性能和用户体验。其中,强缓存协商缓存是两种常见的缓存方式。

获取资源形式状态码发送到服务器
强缓存从缓存获取200 (from cache)否,直接从缓存获取
协商缓存从缓存获取304 (not modified)是,通过服务器来告知缓存是否可用
  • 强缓存的相关字段有expires,cache-control。如果cache-controlexpires同时存在的话,cache-control的优先级高于expires
  • 协商缓存的相关字段有Last-Modified/If-Modified-Since,Etag/If-None-Match
  1. 浏览器第一次发起请求时,本地无缓存,向web服务器发送请求,服务器端响应请求,浏览器端缓存,过程如下:
浏览器请求
无缓存
向web服务器请求
请求响应,缓存协商
呈现

第一次请求时,服务器会将页面最后的修改时间通过last-Modified标识发送给客户端,客户端记录修改时间;服务器还会生成一个Etag,并发送给客户端。

  1. 浏览器后续再次请求:

Web开发中的强缓存和协商缓存策略(一)

  • 浏览器请求某一资源时,会先获取该资源缓存的header信息,然后根据header中的Cache-ControlExpires来判断是否过期。若没过期直接从缓存中获取资源信息,包括缓存的header的信息,所以此次请求不会和服务器进行通信。这里判断是否过期,则是强缓存相关。

  • 如果显示已过期,浏览器会向服务器端发送请求,这个请求会携带第一次请求返回的有关缓存的header字段信息,比如客户端会通过If-None-Match头将先前服务器端发送过来的Etag发送给服务器,服务器会对比这个客户端发过来的Etag是否与服务器相同,若相同,就将If-None-Match的值设置为false,返回状态304,客户端继续使用本地缓存,不解析服务器端返回的数据;若不相同就将If-None-Match的值设为true,返回状态200,客户端重新接收服务器端返回的数据;客户端还会通过If-Modified-Since头将先前服务器端发过来的最后修改时间戳发给服务器,服务器端通过这个时间戳判断客户端的页面是否是最新的,如果不是最新的,则返回最新的内容,否之返回304,客户端继续使用本地缓存。

1.强缓存(本地缓存)

强缓存是指浏览器在第一次请求资源时,将资源缓存到本地,并在下一次请求时直接从缓存中获取,而不是向服务器发送请求。这种缓存方式可以通过设置HTTP头中Cache-ControlExpires字段来实现。其中,Cache-Control可以设置缓存的最大时间,而Expires则是设置缓存的过期时间。当缓存时间未过期时,浏览器会直接从缓存中获取资源,从而提高网站的访问速度。

2.协商缓存(弱缓存)

协商缓存是浏览器在第一次请求资源时,服务器会返回一个包含资源信息的响应头,浏览器会将信息保存在缓存中。下一次请求时,浏览器会将这些信息发送给服务器,服务器会根据这些信息判断资源是否更新。如果没有更新,则返回一个304 Not Modified的响应,告诉浏览器可以直接从缓存中获取资源。这种缓存方式可以通过设置HTTP响应头中的ETagLast-Modified字段来实现。

二. 使用场景

1. 下面是一个前端强缓存的例子

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>前端强缓存示例</title>
</head>
<body>
    <h1>前端强缓存示例</h1>
    <img src="/image.jpg" alt="图片">
    <script src="/script.js"></script>
</body>
</html>
const express = require('express');
const app = express();

// 设置静态文件目录
app.use(express.static('public'));

app.get('/index.html', (req, res) => {
    res.sendFile(__dirname + '/public/index.html');
});

// 设置图片的缓存时间为1小时
app.get('/image.png', (req, res) => {
    res.setHeader('Cache-Control', 'max-age=3600');
    res.sendFile(__dirname + '/public/image.png');
});

// 设置JavaScript文件的缓存时间为1天
app.get('/script.js', (req, res) => {
    res.setHeader('Cache-Control', 'max-age=86400');
    res.sendFile(__dirname + '/public/script.js');
});

// 启动服务器
app.listen(3000, () => {
    console.log('http://127.0.0.1:3000', '正在启动......');
})

在上面的服务器端代码中,使用了Express框架设置了静态文件目录,并分别 设置了图片和JavaScript文件的缓存时间为1小时和1天。当浏览器第一次请求这些资源时,服务器会将资源发给浏览器,并在响应头中设置缓存时间和缓存策略。当浏览器再次请求这些资源时,会检查缓存是否过期,如果没有过期,则直接从缓存中获取资源,否则重新向服务器请求资源。

2. 下面是一个前端协商缓存的例子

const express = require('express');
const app = express();

// 设置静态文件服务
app.use(express.static('public'));

// 设置路由,返回一个JSON数据
app.get('/api/data', (req, res) => {
    const data = { message: 'Hello, world!' };
    // 设置响应头部,包括缓存控制和协商缓存相关的头部
    res.set({
        'Cache-Control': 'public, max-age=3600', // 设置强缓存,缓存时间为1小时
        'Last-Modified': new Date().toUTCString(), // 设置最后修改时间
        'ETag': '123456789' // 设置ETag
    });
    res.json(data);
});

// 设置路由,用于协商缓存
app.get('/api/data2', (req, res) => {
    const data = { message: 'Hello, world!' };
    // 获取请求头部中的If-Modified-Since和If-None-Match字段
    const ifModifiedSince = req.headers['if-modified-since'];
    const ifNoneMatch = req.headers['if-none-match'];
    // 如果两个字段都存在,说明浏览器支持协商缓存
    if (ifModifiedSince && ifNoneMatch) {
        // 比较最后修改时间和ETag是否匹配
        if (ifModifiedSince === new Date().toUTCString() && ifNoneMatch === '123456789') {
            // 如果匹配,返回304 Not Modified状态码,表示资源未修改,可以使用缓存
            res.status(304).end();
            return;
        }
    }
    // 如果不支持协商缓存或者资源已经修改,返回新的数据和响应头部
    res.set({
        'Cache-Control': 'public, max-age=3600',
        'Last-Modified': new Date().toUTCString(),
        'ETag': '123456789'
    });
    res.json(data);
});

// 启动服务器
app.listen(3000, () => {
    console.log('http://127.0.0.1:3000', '正在启动......');
})

在上面的代码中,当浏览器请求/api/data2路由时,服务器会先获取请求头部中的If-Modified-SinceIf-None-Match字段,如果两个字段都存在,说明浏览器支持协商缓存。服务器会比较最后修改时间和ETag是否匹配,如果匹配,返回304 Not Modified状态码,表示资源未修改,可以使用缓存。如果不支持协商缓存或者资源已经修改,服务器会返回新的数据和响应头部,包括Cache-ControlLast-ModifiedETag等头部,用于强缓存和协商缓存。这样,浏览器就可以根据响应头部中的信息来判断是否使用缓存。

三. 总结

在实际开发中,我们可以根据资源的特点和需求来选择合适的缓存方式。

  • 强缓存:适用于不经常更新的静态资源,如图片、CSSJS文件等
  • 协商缓存:适用于经常更新的动态资源,如HTML页面和API接口