当前位置: 首页 > article >正文

浏览器缓存

浏览器缓存是一种存储机制,它允许浏览器将网页的部分或全部内容(如HTML文件、图像、JavaScript文件等)存储在用户的本地设备上。这样,当用户再次访问同一个网站时,浏览器可以从缓存中加载这些资源,而不需要重新从服务器下载,从而加快了页面的加载速度并减少了网络流量。

以下是关于浏览器缓存的一些关键概念和技术细节:

类型

  1. 强制缓存 (MUST-Cache):

    • Expires Header: 指定资源过期的时间点。
    • Cache-Control Header: 包含max-age指令来指定资源的有效时间。
  2. 验证缓存 (Validate Cache):

    • 当资源可能已经过期但仍然可能有效时,浏览器会使用条件请求向服务器验证资源是否已更新。
    • Last-Modified 和 If-Modified-Since Headers: 用于检查资源是否已有新的修改版本。
    • ETag 和 If-None-Match Headers: 使用唯一标识符来确定资源是否改变。

如何工作

浏览器第一次加载资源,服务器返回200,浏览器从服务器下载资源文件,并缓存资源文件和response header,以供下次加载时对比使用;

下次加载资源时,由于强制缓存优先级更高,所以会执行强制缓存策略。

基于Expires字段实现的强缓存

在以前,我们通常会使用响应头的Expires字段去实现强缓存。该字段的作用是,设定一个强缓存时间。在此时间范围内,则命中强缓存,直接从内存(或磁盘)中读取缓存返回。但是该字段存在问题:过度依赖本地时间。

基于Cache-control实现的强缓存(代替Expires的强缓存实现方法)

字段在http1.1中被增加,Cache-control完美解决了Expires本地时间和服务器时间不同步的问题。是当下的项目中实现强缓存的最常规方法。

Cache-control的使用方法很简单,只要在资源的响应头上写上需要缓存多久就好了,单位是秒。

Cache-Control: max-age=3600

该字段还有其他属性,详情见:https://blog.csdn.net/hyupeng1006/article/details/126599764

如果资源过期,则表明强制缓存没有被命中,则开始执行协商缓存策略。

基于last-modified的协商缓存

基于 Last-Modified 的协商缓存是一种机制,用于确定客户端(通常是浏览器)中的缓存副本是否仍然是最新的。这种机制依赖于服务器端的 Last-Modified 响应头和客户端发出的 If-Modified-Since 请求头。其具体策略是:

1.在第一次请求时,服务器返回响应,并在响应头中包含 Last-Modified 字段,指示该资源最后一次被修改的时间;

2.缓存资源;

3.再次发送请求访问相同资源,浏览器在请求头中包含 If-Modified-Since 字段,其值为上次接收到的 Last-Modified 值;

4.服务器检查资源的最后修改时间是否与 If-Modified-Since 中的时间相同;

  • 如果资源没有被修改,则服务器返回一个 304 Not Modified 响应,则客户端直接从缓存中加载资源。
  • 如果资源已被修改,则服务器返回一个新的 200 OK 响应,其中包含新的 Last-Modified 时间戳以及资源的新内容,同时更新缓存。
const http = require('http');
const fs = require('fs');

const server = http.createServer((req, res) => {
  const filePath = 'example.html';
  
  // 获取文件的最后修改时间
  fs.stat(filePath, (err, stats) => {
    if (err) {
      res.writeHead(500);
      res.end('Internal Server Error');
      return;
    }
    
    const lastModifiedTime = stats.mtime.toUTCString();
    const ifModifiedSince = req.headers['if-modified-since'];

    // 检查是否需要发送304响应
    if (ifModifiedSince === lastModifiedTime) {
      res.writeHead(304);
      res.end();
    } else {
      // 发送200响应
      res.writeHead(200, { 'Last-Modified': lastModifiedTime });
      fs.createReadStream(filePath).pipe(res);
    }
  });
});

server.listen(3000, () => {
  console.log('Server running on port 3000');
});
优点
  • 减少了不必要的数据传输,提高了性能。
  • 降低了服务器负载。
缺点
  • 只能精确到秒,如果资源在同一秒内多次修改,可能会导致缓存问题。
  • 如果资源经常在短时间内被修改,可能会导致额外的网络请求。
基础ETag的协商缓存

为了克服 Last-Modified 的缺点,通常会结合使用 ETagETag 提供了一个唯一的标识符(文件指纹)来区分资源的不同版本,即使是同一秒内的修改也能被识别出来。

其具体流程与last-modified类似,只不过是将标识符赋给if-None-Match字段,并进行对比。

const http = require('http');
const fs = require('fs');

const server = http.createServer((req, res) => {
  const filePath = 'example.html';
  
  // 获取文件的哈希值作为ETag
  fs.readFile(filePath, (err, data) => {
    if (err) {
      res.writeHead(500);
      res.end('Internal Server Error');
      return;
    }

    const etag = `"${Buffer.from(data).toString('base64')}"`; // 使用文件内容的Base64编码作为ETag
    const ifNoneMatch = req.headers['if-none-match'];

    // 检查是否需要发送304响应
    if (ifNoneMatch === etag) {
      res.writeHead(304);
      res.end();
    } else {
      // 发送200响应
      res.writeHead(200, { 'ETag': etag });
      res.end(data);
    }
  });
});

server.listen(3000, () => {
  console.log('Server running on port 3000');
});

总结:

浏览器缓存有许多优势,主要体现在以下几个方面:

  1. 提高加载速度:

    • 浏览器可以直接从缓存加载资源,而无需从服务器获取,这显著加快了页面的加载速度。
  2. 减少网络流量:

    • 由于减少了重复的数据传输,网络流量得到节约,这对于移动网络环境尤为重要。
  3. 降低服务器负载:

    • 服务器不必频繁地处理重复的请求,减轻了服务器的压力。
  4. 改善用户体验:

    • 快速加载的页面让用户感觉更流畅,提升了整体的用户体验。
  5. 节省带宽成本:

    • 对于需要支付带宽费用的网站运营者来说,减少数据传输可以节省成本。
  6. 离线访问能力:

    • 即使在网络连接不稳定或断开的情况下,用户仍然可以访问之前缓存的内容。
  7. 提高应用性能:

    • 对于单页应用(SPA)和 Progressive Web Apps (PWA),合理的缓存策略能够提升应用的响应速度和可用性。
  8. 减少延迟:

    • 通过减少从远程服务器获取数据所需的往返时间,缓存可以降低延迟。
  9. 支持快速重载:

    • 用户刷新页面时,浏览器可以从缓存中快速加载页面,而不是每次都从服务器获取最新内容。

http://www.kler.cn/a/283007.html

相关文章:

  • 用MVVM设计模式提升WPF开发体验:分层架构与绑定实例解析
  • 【数据结构与算法】第12课—数据结构之归并排序
  • python 同时控制多部手机
  • 贪心算法day03(最长递增序列问题)
  • Window下PHP安装最新sg11(php5.3-php8.3)
  • 【go从零单排】JSON序列化和反序列化
  • 网络安全-安全渗透简介和安全渗透环境准备
  • 【CSP:202109-2】非零段划分(Java)
  • 4.sklearn-K近邻算法、模型选择与调优
  • MySQL集群技术1——编译部署mysql
  • “重启就能解决一切问题”,iPhone重启方法大揭秘
  • 解决:无法从域控制器读取配置信息
  • 2024.8.29 C++
  • C#面:ASP.NET MVC 中还有哪些注释属性用来验证?
  • RKNPU2从入门到实践 ---- 【8】借助 RKNN Toolkit lite2 在RK3588开发板上部署RKNN模型
  • 设计模式--装饰器模式
  • 理解torch.argmax() ,我是错误的
  • 融资和融券分别是什么意思,融资融券开通后能融到多少资金?
  • Datawhale X 李宏毅苹果书 AI夏令营_深度学习基础学习心得Task2.2
  • Java 入门指南:Java NIO —— Selector(选择器)
  • 【hot100篇-python刷题记录】【搜索二维矩阵】
  • 分布式锁的实现:ZooKeeper 的解决方案
  • hive数据迁移
  • 低代码革命:JNPF平台如何简化企业应用开发
  • Linux 中的中断响应机制
  • TCP keepalive和HTTP keepalive区别