告别重复加载:掌握浏览器强缓存与协商缓存策略

浏览器缓存的核心思想

核心目的是减少网络请求的发起次数减少服务器需要传输的数据量,从而显著提升页面加载速度,减轻服务器压力,提升用户体验。

浏览器的缓存策略主要分为两个阶段:强缓存协商缓存

分层解析

强缓存 (Strong Cache)

核心: 不问服务器,直接用自己的副本。
目标: 根本不会发起HTTP请求。

  • 工作原理 :浏览器在请求一个资源时,会先检查本地缓存。如果发现该资源的缓存副本,并且它尚未过期 ,浏览器就会直接使用这个副本,完全不会向服务器发送任何请求 。这个过程发生在浏览器内部,是静默的,因此在开发者工具的Network面板中看到该请求的状态码是 200 (from disk cache)表示在磁盘当中打开多次的资源200 (from memory cache)表示在内存当中,浏览器关闭则释放

  • 如何控制 - HTTP Header

    • Expires (HTTP/1.0):一个绝对的过期时间(GMT格式),例如 Expires: Wed, 21 Oct 2024 07:28:00 GMT。缺点是如果客户端和服务器时间不一致,会导致缓存判断错误。

    • Cache-Control (HTTP/1.1):优先级高于Expires,提供了更灵活、更精确的控制。常用指令:

      • max-age=:设置缓存的最大有效时间,单位是秒(相对时间)。例如 Cache-Control: max-age=3600 表示资源1小时内有效。
      • public:响应可以被任何对象(客户端、代理服务器等)缓存。
      • private:响应只能被单个用户(浏览器)缓存,不能被代理服务器缓存。
      • no-cache不是不缓存 ,而是使用缓存前,必须先向服务器验证(即跳过强缓存,直接进入协商缓存阶段)。
      • no-store真正的不缓存,完全不使用任何缓存策略。每次都要从服务器重新获取。

协商缓存 (Negotiation Cache)

核心: 问问服务器,我这个副本还能不能用。
目标: 可能省去传输响应体的开销。

  • 触发条件 :当强缓存失效(过期)时,浏览器就会启用协商缓存。

  • 工作原理 :浏览器会向服务器发起一个条件性请求,携带一些"验证字段"。服务器根据这些字段判断客户端的缓存副本是否依然有效。

    • 如果有效,服务器返回 304 Not Modified,响应体为空,告诉浏览器:"你的副本没变,继续用吧!"。
    • 如果失效,服务器返回 200 OK 和完整的资源内容。
  • 如何控制 - 成对的HTTP Header

    • 第一对:Last-Modified / If-Modified-Since

      • 服务器响应Last-Modified: Wed, 21 Oct 2024 07:28:00 GMT(资源最后修改时间)。
      • 浏览器请求 :下次请求时,带上 If-Modified-Since: [上次收到的Last-Modified值]
      • 缺点:精度到秒,如果文件在1秒内多次改动,无法识别;有时候文件内容没变,但修改时间变了(比如touch了一下),会导致不必要的重新下载。
    • 第二对:ETag / If-None-Match (优先级更高)

      • 服务器响应ETag: "a5d7f8e3q1w2"(一个唯一标识符,通常是文件的哈希值或版本号)。
      • 浏览器请求 :下次请求时,带上 If-None-Match: [上次收到的ETag值]
      • 优点 :比Last-Modified更精确,能感知文件内容的微小变化。完美解决上述"秒级修改"和"仅修改时间"的问题。

对比和总结

特性 强缓存 协商缓存
是否发请求 不发
目标 完全不请求,极致速度 可能省去响应体(返回304)
状态码 200 (from cache) 304 Not Modified
控制字段 Cache-Control, Expires ETag/If-None-Match, Last-Modified/If-Modified-Since
优先级 先执行 强缓存失效后执行

完整的缓存决策流程可以概括为:

浏览器加载资源时 -> 先看有没有缓存 -> 有缓存,再看Cache-Control/Expires是否过期 -> 未过期 ,强缓存生效 -> 已过期 ,则发起请求带上If-None-Match/If-Modified-Since -> 服务器验证 -> 有效返回304,无效返回200和新资源。

实际应用中

在实际的前端项目中,我们通常会通过Webpack、Vite等构建工具来管理静态资源的缓存:

  1. 哈希文件名 :我们对静态资源(JS, CSS, 图片)的文件名使用内容哈希(如 app.a5d7f8e3.js)。这样一旦文件内容改变,文件名就会变,相当于请求了一个全新的URL,因此可以设置非常长的强缓存时间(比如一年) ,即 Cache-Control: max-age=31536000。这是性能最佳实践。
  2. HTML文件 :HTML是入口文件,通常我们将其设置为 Cache-Control: no-cache 或较短的最大存活时间,确保用户能及时获取到最新的页面和最新的资源链接。

通过这种策略,我们既保证了静态资源的高缓存命中率,又能保证内容的及时更新。


如果使用了强缓存出现了BUG(项目更新之后,用户浏览器还在用旧文件,导致页面显示不正常)

  1. 资源文件名(指纹化)

    这是最彻底、最推荐的做法。原理是:如果文件内容变了,那么文件的名字也变,对于浏览器来说,这就是一个全新的资源,自然会发起新的请求。 实现方法是在文件名中嵌入一个"指纹"(Hash),这个指纹根据文件内容计算得出。内容一变,指纹必变。

    • 原始文件: styles.css
    • 指纹化后: styles.a1b2c3d4.css (哈希值随内容变化)

    如何实现指纹化?

    你通常不需要手动做这件事,现代前端构建工具(Webpack, Vite, Rollup, Parcel 等)可以自动完成。

    • Webpack: 使用 [contenthash] 占位符。

      javascript

      css 复制代码
      // webpack.config.js
      output: {
        filename: '[name].[contenthash].js',
        chunkFilename: '[name].[contenthash].chunk.js',
      }
    • Vite: 生产环境构建默认就会为静态资源添加哈希后缀。

      配套的 HTML 处理:

      构建工具在生成带哈希的文件名后,也会自动更新 HTML 中引用的路径,从 <link href="styles.css"> 变成 <link href="styles.a1b2c3d4.css">

      服务器配置:

      为你所有的静态资源(JS, CSS, 图片,字体等)设置长期强缓存。

      bash 复制代码
      # Nginx 配置示例
      location /static/ {
          alias /path/to/your/static/files/;
          # 设置一年强缓存
          expires 1y;
          add_header Cache-Control "public, immutable";
      }

      注意看,我们缓存的是 /static/ 目录下的文件,这些文件都是被指纹化的。非指纹化的文件(如 index.html绝对不能设置强缓存。

      总结与最佳实践流程

    方案 策略 适用场景 优点 缺点
    文件名指纹化 根本方案 所有新项目和生产环境 一劳永逸,完美平衡性能与更新 需要构建工具支持
    CDN 刷新 紧急预案 线上出现紧急 bug 生效快,针对性强 是补救措施,非预防手段
    查询字符串 临时方案 快速测试或紧急热修复 简单,无需工具链 缓存可靠性有争议
相关推荐
Java水解22 分钟前
JAVA经典面试题附答案(持续更新版)
java·后端·面试
恋猫de小郭23 分钟前
对于普通程序员来说 AI 是什么?AI 究竟用的是什么?
前端·flutter·ai编程
大怪v41 分钟前
前端:人工智能?我也会啊!来个花活,😎😎😎“自动驾驶”整起!
前端·javascript·算法
我是天龙_绍42 分钟前
vue3 props 如何写ts,进行类型标注
前端
叫我詹躲躲1 小时前
n8n 自动化工作流平台完整部署
前端·langchain·领域驱动设计
洛小豆2 小时前
在Java中,Integer.parseInt和Integer.valueOf有什么区别
java·后端·面试
遂心_2 小时前
为什么 '1'.toString() 可以调用?深入理解 JavaScript 包装对象机制
前端·javascript
IT_陈寒3 小时前
JavaScript 性能优化:5 个被低估的 V8 引擎技巧让你的代码快 200%
前端·人工智能·后端
岛风风3 小时前
关于手机的设备信息
前端
ReturnTrue8683 小时前
nginx性能优化之Gzip
前端