浏览器缓存策略与服务端实现

引言

Web 应用中,网页的加载速度是至关重要的,也是衡量Web应用的重要指标。针对此,浏览器缓存是一种常用的优化技术,可以显著提高用户的访问体验和网页的响应速度

性能影响

缓存对Web应用影响颇深:

  • 减少网络请求:缓存可以存储已经下载过的资源副本,当浏览器再次请求相同的资源时,可以直接使用缓存副本,避免了网络请求的开销。这样可以减少页面加载时间和网络流量,提高网页的响应速度和性能。
  • 降低服务器负载:当浏览器使用缓存副本时,减少了服务器的负载。特别是对于大型网站和高流量的应用程序,缓存可以显著减轻服务器的压力,提高系统的可扩展性和稳定性。
  • 提高页面加载速度:缓存可以减少页面加载时间,因为浏览器不需要重新下载和解析已经缓存的资源。这对于移动设备和弱网环境尤为重要,可以提供更好的用户体验。
  • 减少带宽消耗:缓存可以减少网络流量,可以降低用户的数据使用费用。

未使用缓存:

强缓存生效:

协商缓存生效:

强缓存

判断资源是否过期,如果没有过期则使用缓存,否则重新请求。常用于静态资源。

两种实现方式:

  • Cache-Control:通过设置 Cache-Control 响应头来控制强缓存。常见的 Cache-Control 值有:

    • public:响应可以被任何缓存(包括浏览器缓存和 CDN)缓存。
    • private:响应只能被浏览器缓存缓存,不允许 CDN 缓存。
    • max-age=<seconds>:设置缓存的最大有效时间,单位为秒。
  • Expires:通过设置 Expires 响应头来控制强缓存。它是一个具体的过期日期时间,表示资源在该时间之前有效。但是,Expires 是一个绝对时间,它依赖于客户端和服务器时间的同步

区别:

  • Cache-Control 是 HTTP/1.1 中引入的缓存控制头,而 Expires 是 HTTP/1.0 中引入的缓存过期时间头。
  • Cache-Control 提供了更多的缓存控制选项和灵活性。它可以指定缓存的行为,如缓存的最大有效时间、是否允许缓存的共享等。常见的 Cache-Control 值包括 publicprivatemax-age 等。而 Expires 只能指定一个具体的过期日期时间,它是一个绝对时间,依赖于客户端和服务器时间的同步。
  • Cache-Control 支持更多的缓存控制指令,如 no-cacheno-storemust-revalidate 等,可以更细粒度地控制缓存行为。而 Expires 只能指定过期时间,没有其他控制选项。Cache-Control 的优先级高于 Expires,应该优先考虑。

协商缓存

正式请求发起前向服务器发起一个请求,用来判断当前缓存是否有效。如果有效则使用缓存,否则重新请求。

两种实现方式:

  • Last-Modified 和 If-Modified-Since:服务器在响应头中返回 Last-Modified 字段,表示资源的最后修改时间。当浏览器再次请求该资源时,会发送一个 If-Modified-Since 请求头,其中包含上次获取资源时的 Last-Modified 值。如果资源的最后修改时间与 If-Modified-Since 的值相同,服务器会返回 304 Not Modified 响应,表示资源未发生变化,浏览器可以使用缓存副本。
  • ETag 和 If-None-Match:服务器在响应头中返回 ETag 字段,表示资源的唯一标识符(通常是哈希值或版本号)。当浏览器再次请求该资源时,会发送一个 If-None-Match 请求头,其中包含上次获取资源时的 ETag 值。如果资源的 ETag 值与 If-None-Match 的值相同,服务器会返回 304 Not Modified 响应。

区别:

  • Last-Modified 是一个表示资源最后修改时间的响应头字段,由服务器在响应中发送。表示资源的最后修改时间。 ETag 是一个表示资源唯一标识符的响应头字段,由服务器生成并在响应中发送。通常是一个哈希值或版本号,用于标识资源的特定版本。
  • Last-Modified 是一个相对简单的机制,它只关注资源的最后修改时间。如果资源的最后修改时间发生变化,浏览器会重新请求整个资源。而 ETag 提供了更精确的资源识别方式,不仅考虑资源的最后修改时间,还可以考虑其他因素,如内容的哈希值。如果 ETag 值发生变化,浏览器会重新请求整个资源。
  • ETag 的优先级高于 Last-Modified。如果服务器同时返回了 ETagLast-Modified,浏览器会优先使用 ETag 进行缓存验证。
  • ETag 相对于 Last-Modified 具有更高的准确性和可靠性。因为有些情况下,资源的最后修改时间可能不准确(例如,文件复制或移动时可能丢失最后修改时间) ,而 ETag 是由服务器生成的,不依赖于文件系统的时间戳。
  • ETag 的计算成本相对较高,因为它需要服务器生成和发送一个唯一标识符。而 Last-Modified 只需要读取文件的最后修改时间即可。

最佳实践

简单实现

js 复制代码
@Controller('file')
export class FileController {
    constructor(private readonly fileService: FileService) {}

    sendFile(
        stat: fs.Stats,
        filePath: string,
        res: ServerResponse,
        eTag: string,
    ) {
        res.writeHead(200, {
            'Content-Type': 'image/jpeg',
            'Content-Length': stat.size,
            ETag: eTag,
        });
        fs.createReadStream(filePath).pipe(res);
    }


    getFileEtag(filePath: string) {
        const hash = createHash('sha1');
        hash.update(fs.readFileSync(filePath));
        return hash.digest('hex');
    }

    @Get()
    getFile(@Res() res: ServerResponse, @Headers() Headers) {
        const filePath = 'public/cat.jpg';
        try {
            const stat = fs.statSync(filePath);
            if (stat.isFile()) {
                const eTag = this.getFileEtag(filePath);
                res.setHeader('Cache-Control', 'max-age=100');
                if (Headers['if-none-match'] === eTag) {
                    res.statusCode = 304;
                    res.end();
                    return;
                }
               return this.sendFile(stat, filePath, res, eTag);
            }
            res.end('not found');
        } catch (error: any) {
            console.log(error);
            res.end('error');
        }
    }
}
相关推荐
姑苏洛言23 分钟前
编写产品需求文档:黄历日历小程序
前端·javascript·后端
知识分享小能手1 小时前
Vue3 学习教程,从入门到精通,使用 VSCode 开发 Vue3 的详细指南(3)
前端·javascript·vue.js·学习·前端框架·vue·vue3
姑苏洛言1 小时前
搭建一款结合传统黄历功能的日历小程序
前端·javascript·后端
你的人类朋友2 小时前
🤔什么时候用BFF架构?
前端·javascript·后端
知识分享小能手3 小时前
Bootstrap 5学习教程,从入门到精通,Bootstrap 5 表单验证语法知识点及案例代码(34)
前端·javascript·学习·typescript·bootstrap·html·css3
一只小灿灿3 小时前
前端计算机视觉:使用 OpenCV.js 在浏览器中实现图像处理
前端·opencv·计算机视觉
前端小趴菜053 小时前
react状态管理库 - zustand
前端·react.js·前端框架
Jerry Lau3 小时前
go go go 出发咯 - go web开发入门系列(二) Gin 框架实战指南
前端·golang·gin
我命由我123454 小时前
前端开发问题:SyntaxError: “undefined“ is not valid JSON
开发语言·前端·javascript·vue.js·json·ecmascript·js