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

引言

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');
        }
    }
}
相关推荐
小周同学@31 分钟前
谈谈对this的理解
开发语言·前端·javascript
Wiktok34 分钟前
Pyside6加载本地html文件并实现与Javascript进行通信
前端·javascript·html·pyside6
一只小风华~38 分钟前
Vue:条件渲染 (Conditional Rendering)
前端·javascript·vue.js·typescript·前端框架
柯南二号40 分钟前
【大前端】前端生成二维码
前端·二维码
程序员码歌1 小时前
明年35岁了,如何破局?说说心里话
android·前端·后端
博客zhu虎康2 小时前
React Hooks 报错?一招解决useState问题
前端·javascript·react.js
灰海2 小时前
vue中通过heatmap.js实现热力图(多个热力点)热区展示(带鼠标移入弹窗)
前端·javascript·vue.js·heatmap·heatmapjs
王源骏3 小时前
LayaAir鼠标(手指)控制相机旋转,限制角度
前端
大虾写代码3 小时前
vue3+TS项目配置Eslint+prettier+husky语法校验
前端·vue·eslint
wordbaby3 小时前
用 useEffectEvent 做精准埋点:React analytics pageview 场景的最佳实践与原理剖析
前端·react.js