还搞不明白浏览器缓存?

一:前言

浏览器缓存浏览器储存 是不一样的,友友们不要混淆,关于浏览器储存,具体可以看这篇文章 : 一篇打通浏览器储存

这里大概介绍一下:

cookies localStorage sessionStorage IndexedDB
服务端设置 一直存在 页面关闭就消失 一直存在
4K 5M 5M 无限大
自动携带在http请求头中 不参与后端 不参与后端 不参与后端
默认不允许跨域,但可以设置 可跨域 可跨域 可跨域

二:强缓存

强缓存是指浏览器在请求资源时,如果本地有符合条件的缓存,那么浏览器会直接使用缓存而不会向服务器发送新的请求。这可以通过设置 Cache-ControlExpires 响应头来实现。

2.1:Cache-Control 头详解

Cache-Control 是一个非常强大的HTTP头部字段,它包含多个指令,用以控制缓存的行为:

  • max-age:指定从响应生成时间开始,该资源可被缓存的最大时间(秒数)。
  • s-maxage :类似于 max-age,但仅对共享缓存(如代理服务器)有效。
  • public:表明响应可以被任何缓存存储,即使响应通常是私有的。
  • private:表明响应只能被单个用户缓存,不能被共享缓存存储。
  • no-cache:强制缓存在使用前必须先验证资源是否仍然新鲜。
  • no-store:禁止缓存该响应,每次请求都必须获取最新数据。
  • must-revalidate:一旦资源过期,必须重新验证其有效性。

例如,通过设置 Cache-Control: max-age=86400,可以告诉浏览器这个资源可以在本地缓存24小时。在这段时间内,如果再次访问相同URL,浏览器将直接使用缓存中的副本,而不与服务器通信。

2.2:Expires 头

Expires 是一个较旧的头部字段,用于设定资源过期的具体日期和时间。尽管现在推荐使用 Cache-Control,但在某些情况下,Expires 仍然是有效的。Expires 的值是一个绝对的时间点,而不是相对时间。例如:

js 复制代码
Expires: Wed, 09 Oct 2024 18:29:00 GMT

2.3:浏览器默认行为

当用户通过地址栏直接请求资源时,浏览器通常会自动添加 Cache-Control: no-cache 到请求头中。这意味着即使资源已经存在于缓存中,浏览器也会尝试重新验证资源新鲜度,以确保用户看到的是最新的内容。

三:协商缓存

协商缓存发生在资源的缓存条目已过期设置了 no-cache 指令的情况下。这时,浏览器会向服务器发送请求,并携带上次请求时收到的一些信息,以便服务器决定是否返回完整响应或只是确认没有更新。

3.1:Last-Modified/If-Modified-Since

后端服务器可以为每个资源设置 Last-Modified 头部,表示资源最后修改的时间。当下一次请求同一资源时,浏览器会在请求头中加入 If-Modified-Since 字段,其值为上次接收到的 Last-Modified 值。服务器检查这个时间戳,如果资源自那以后没有改变,则返回304 Not Modified状态码,指示浏览器使用缓存中的版本。

3.2:ETag/If-None-Match

ETag 提供了一种更精确的方法来检测资源是否发生变化。它是基于文件内容计算出的一个唯一标识符。当客户端请求资源时,服务器会在响应头中提供一个 ETag 值。下次请求时,浏览器会发送 If-None-Match 头部,包含之前接收到的 ETag。如果资源未改变,服务器同样返回304状态码;如果有变化,则返回完整的资源及新的 ETag 值。

3.3:比较 Last-Modified 和 ETag

虽然 Last-Modified 简单易用,但它基于时间戳,可能会受到时钟同步问题的影响。相比之下,ETag 更加准确,因为它依赖于资源的实际内容。然而,ETag 计算可能需要更多的服务器处理能力。

四:缓存选择

合理的缓存策略能够显著提升网站性能和用户体验。例如,静态资源(如图片、CSS、JavaScript文件)适合设置较长的缓存时间,而动态内容则需谨慎对待,避免缓存不适当的信息。

  • 使用工具如 Chrome DevTools 来分析页面加载时间和缓存效果。
  • 对不同类型的资源设置合适的 Cache-Control 参数。
  • 注意安全性和隐私保护,确保敏感数据不会被错误地缓存。

五:使用示例

  1. 引入必要的模块 :导入 http, path, fsmime 模块。
  2. 创建HTTP服务器 :使用 http.createServer 创建一个HTTP服务器。
  3. 处理请求
    • 根据请求的URL生成文件路径。
    • 检查文件是否存在。
    • 如果是目录,指向该目录下的 index.html 文件。
  4. 处理协商缓存
    • 获取请求头中的 If-Modified-Since 字段。
    • 比较 If-Modified-Since 与文件的最后修改时间。
  5. 读取文件并发送响应
    • 读取文件内容。
    • 设置响应头(包括 Content-Type, Cache-Control, Last-Modified, ETag)。
    • 发送响应体。
  6. 启动服务器:监听3000端口并启动服务器。

server.js:

js 复制代码
const http = require('http'); // 引入HTTP模块
const path = require('path'); // 引入路径处理模块
const fs = require('fs'); // 引入文件系统模块
const mime = require('mime'); // 引入MIME类型模块

// 创建一个HTTP服务器
const server = http.createServer((req, res) => {
  // console.log(req.url); // /index.html    //   /assets/image/logo.png
  
  // 根据请求的URL生成文件路径
  let filePath = path.resolve(__dirname, path.join('www', req.url));

  // 检查文件或目录是否存在
  if (fs.existsSync(filePath)) {
    const stats = fs.statSync(filePath); // 获取该路径对应的资源状态信息
    // console.log(stats);
    
    const isDir = stats.isDirectory(); // 判断是否是文件夹
    const { ext } = path.parse(filePath); // 获取文件扩展名
    if (isDir) {
      // 如果是目录,则指向该目录下的 index.html 文件
      filePath = path.join(filePath, 'index.html');
    }

    // +++++ 获取前端请求头中的if-modified-since
    const timeStamp = req.headers['if-modified-since']; // 获取请求头中的 If-Modified-Since 字段
    let status = 200; // 默认响应状态码为200
    if (timeStamp && Number(timeStamp) === stats.mtimeMs) { // 如果 If-Modified-Since 存在且与文件最后修改时间相同
      status = 304; // 设置响应状态码为304,表示资源未变更
    }

    // 如果不是目录且文件存在
    if (!isDir && fs.existsSync(filePath)) {
      const content = fs.readFileSync(filePath); // 读取文件内容
      res.writeHead(status, {
        'Content-type': mime.getType(ext), // 设置 Content-Type 头
        'cache-control': 'max-age=86400',  // 设置缓存控制为一天
        // 'last-modified': stats.mtimeMs,  // 资源最新修改时间(可选)
        // 'etag': '由文件内容生成的hash'  // 文件指纹(可选)
      });
      res.end(content); // 发送文件内容作为响应体
    } 
  }
  
});

// 启动服务器,监听3000端口
server.listen(3000, () => {
  console.log('listening on port 3000');
});r.listen(3000, () => {
  console.log('listening on port 3000');
});

index.html:

js 复制代码
<body>
  <h1>midsummer</h1>
  <img src="assets/image/1.png" alt="">
</body>

项目结构如下图,友友们自行准备一张图片,将项目npm init -y 初始化为后端项目,之后下载mime@3 包,在终端输入npx nodemon server.js运行起来,在浏览器中查看http://localhost:3000/index.html ,观察效果。在检查中的网络里看缓存效果,同时友友们可以更改图片或者缓存方式,体验下不同的浏览器缓存方式

相关推荐
潜洋1 小时前
Spring Boot教程之五:在 IntelliJ IDEA 中运行第一个 Spring Boot 应用程序
java·spring boot·后端
St_Ludwig2 小时前
C语言 蓝桥杯某例题解决方案(查找完数)
c语言·c++·后端·算法·游戏·蓝桥杯
vener_2 小时前
LuckySheet协同编辑后端示例(Django+Channel,Websocket通信)
javascript·后端·python·websocket·django·luckysheet
计算机毕设孵化场2 小时前
计算机毕设-基于springboot的多彩吉安红色旅游网站的设计与实现(附源码+lw+ppt+开题报告)
vue.js·spring boot·后端·计算机外设·课程设计·计算机毕设论文·多彩吉安红色旅游网站
爪哇学长2 小时前
解锁API的无限潜力:RESTful、SOAP、GraphQL和Webhooks的应用前景
java·开发语言·后端·restful·graphql
战神刘玉栋3 小时前
《SpringBoot、Vue 组装exe与套壳保姆级教学》
vue.js·spring boot·后端
码到成功>_<4 小时前
Spring Boot实现License生成和校验
数据库·spring boot·后端
Ztiddler5 小时前
【npm设置代理-解决npm网络连接error network失败问题】
前端·后端·npm·node.js·vue
货拉拉技术5 小时前
多元消息融合分发平台
javascript·后端·架构
醒过来摸鱼5 小时前
【Golang】协程
开发语言·后端·golang