深入理解浏览器缓存:强缓存与协商缓存

在前端性能优化领域,浏览器缓存是绕不开的关键技术。它就像浏览器为资源搭建的 “临时仓库”,能让重复访问的页面跳过网络请求,直接从本地加载资源,大幅减少加载时间、降低服务器压力。但缓存并非 “一刀切”,而是分为强缓存协商缓存两种核心机制,二者的触发逻辑、优先级和应用场景截然不同。今天我们就从原理、代码示例到实际应用,全面拆解浏览器缓存。

一、浏览器缓存的核心价值:为什么需要它?

在正式讲解两种缓存机制前,先明确缓存的核心作用 —— 解决 “重复请求” 的性能浪费问题。当用户第一次访问网页时,浏览器会从服务器下载 HTML、CSS、JS、图片等资源;若用户刷新页面或再次访问,若没有缓存,浏览器会重复发起相同请求,这会导致:

  1. 加载速度慢:重复下载相同资源,增加页面白屏时间;

  2. 带宽消耗大:用户流量和服务器带宽被无效占用;

  3. 服务器压力高:大量重复请求增加服务器负载。

而浏览器缓存通过 “本地存储 + 条件判断”,让资源复用更智能,是前端性能优化中 “性价比最高” 的手段之一。

二、强缓存:无需协商,直接复用

强缓存是优先级最高的缓存机制:当浏览器访问资源时,会先检查本地是否有该资源的 “缓存有效期”,若未过期,则直接从本地加载(完全不发起网络请求),只有过期时才会向服务器请求新资源。

1. 强缓存的实现:两大 HTTP 响应头

强缓存通过服务器返回的HTTP 响应头控制,核心有两个:Cache-Control(HTTP/1.1)和Expires(HTTP/1.0,已逐步被替代)。

(1)Cache-Control:现代浏览器的首选

Cache-Control是当前控制强缓存的主流头,支持多种指令,常见配置如下:

  • max-age=xxx:缓存有效期,单位为秒(s)。例如max-age=3600表示资源在 1 小时内有效;

  • public:资源可被浏览器、CDN 等所有中间缓存存储(默认值);

  • private:资源仅能被用户浏览器缓存,CDN 等中间节点无法缓存(常用于用户个性化资源,如登录后的页面);

  • no-store:完全禁用缓存,每次都必须从服务器重新下载(常用于实时性要求极高的资源,如股票行情)。

示例:服务器返回的响应头中包含以下配置,代表资源在 30 分钟内有效:

HTTP/1.1 200 OK

Cache-Control: max-age=1800

Content-Type: text/css

Content-Length: 1024

此时浏览器第一次加载 CSS 后,会将资源存入 “内存缓存”(临时,关闭标签页失效)或 “磁盘缓存”(持久,关闭浏览器仍存在);30 分钟内再次访问该页面,浏览器会直接从本地缓存加载 CSS,Network 面板中该资源的状态码为200 OK (from cache)

(2)Expires:HTTP/1.0 的遗留方案

Expires通过指定一个绝对时间来定义缓存有效期,例如:

HTTP/1.1 200 OK

Expires: Wed, 01 Oct 2025 12:00:00 GMT

Content-Type: image/png

表示资源在 2025 年 10 月 1 日 12:00 前有效。

Expires有明显缺陷:它依赖客户端时间(即用户电脑的系统时间),若用户手动修改系统时间(如将时间调后 1 年),会导致缓存提前失效或无限期生效,因此现代项目更推荐使用Cache-Control

注意:若

Cache-Control

Expires

同时存在,

Cache-Control 优先级更高

2. 强缓存的代码示例:Nginx 配置

实际项目中,我们通常通过服务器(如 Nginx)配置Cache-Control。以下是 Nginx 配置示例,为不同类型的资源设置不同缓存时长:

server {
    listen 80;
    server_name example.com;

    # 静态资源强缓存(7天)
    location ~* \.(css|js|png|jpg|jpeg|gif|svg|ico|woff2)$ {
        root /usr/share/nginx/html;
        # 强化缓存头设置
        add_header Cache-Control "public, max-age=604800, immutable";
        expires 7d;
        # 添加版本控制建议
        access_log off;
    }

    # HTML文件缓存策略(优先协商缓存)
    location ~* \.html$ {
        root /usr/share/nginx/html;
        add_header Cache-Control "no-cache, must-revalidate";
        # 添加ETag支持协商缓存
        etag on;
    }

    # 新增:API接口不缓存
    location ~* \.php$ {
        add_header Cache-Control "no-store, no-cache, must-revalidate";
    }
}

上述配置中:

  • CSS、JS、图片等静态资源会被缓存 7 天,7 天内重复访问无需请求服务器;

  • HTML 文件设置no-cache,表示不启用强缓存,每次访问都会发起请求(但会触发协商缓存)。

三、协商缓存:需与服务器 “商量” 是否复用

当强缓存失效(资源过期或配置了no-cache)时,浏览器会发起条件请求:携带资源的 “唯一标识” 向服务器询问 “当前资源是否有更新?若没更新,我就用本地缓存”。服务器根据标识判断资源状态,决定返回 “新资源” 或 “复用缓存”。

1. 协商缓存的实现:两组 HTTP 头配对

协商缓存通过 “请求头 + 响应头” 的配对实现,核心有两组:Last-Modified/If-Modified-Since(基于时间)和ETag/If-None-Match(基于内容)。

(1)Last-Modified / If-Modified-Since:基于修改时间
  • 第一次请求:服务器返回资源时,通过Last-Modified头告诉浏览器 “该资源的最后修改时间”,例如:
HTTP/1.1 200 OK

Last-Modified: Tue, 30 Sep 2025 08:30:00 GMT

Content-Type: text/js

浏览器将资源和Last-Modified时间存入缓存。

  • 后续请求:强缓存失效后,浏览器会在请求头中携带If-Modified-Since,值为之前保存的Last-Modified时间,向服务器询问:“资源在这个时间后是否有修改?”
GET /index.js HTTP/1.1

Host: example.com

If-Modified-Since: Tue, 30 Sep 2025 08:30:00 GMT
  • 服务器判断

    • 若资源未修改(当前修改时间 ≤ If-Modified-Since):返回304 Not Modified不携带资源内容,浏览器直接复用本地缓存;

    • 若资源已修改(当前修改时间 > If-Modified-Since):返回200 OK和新资源,并更新Last-Modified时间。

缺陷:若资源内容未变,但修改时间被修改(如手动编辑文件但未改内容),会导致服务器误判 “资源已更新”,返回新资源,浪费带宽。

(2)ETag / If-None-Match:基于内容哈希(更可靠)

为解决 “时间误判” 问题,ETag应运而生 —— 它是服务器对资源内容计算的唯一哈希值(如 MD5、SHA1),资源内容只要有任何变化,哈希值就会改变。

  • 第一次请求:服务器返回资源时,通过ETag头返回资源的哈希值,例如:
HTTP/1.1 200 OK

ETag: "5f8d7a3e-1234" # 哈希值示例,格式由服务器决定

Content-Type: text/css

浏览器将资源和ETag存入缓存。

  • 后续请求:强缓存失效后,浏览器在请求头中携带If-None-Match,值为之前保存的ETag,向服务器询问:“当前资源的哈希值是否和这个一致?”
GET /style.css HTTP/1.1

Host: example.com

If-None-Match: "5f8d7a3e-1234"
  • 服务器判断

    • 若哈希值一致(资源未变):返回304 Not Modified,浏览器复用缓存;

    • 若哈希值不一致(资源已变):返回200 OK和新资源,并更新ETag

优势:完全基于资源内容判断,避免了 “时间修改但内容未变” 的误判,是更可靠的协商缓存方案。

注意:若两组头同时存在,

ETag/If-None-Match 优先级更高

2. 协商缓存的代码示例:Node.js 实现

以下是用 Node.js(Express 框架)实现协商缓存的示例,同时支持Last-ModifiedETag

const express = require('express');

const fs = require('fs');

const path = require('path');

const { createHash } = require('crypto');

const app = express();

const port = 3000;

// 计算文件的ETag(基于文件内容的MD5哈希)

function getETag(filePath) {

 const content = fs.readFileSync(filePath);

 const md5 = createHash('md5').update(content).digest('hex');

 return `"${md5}"`; // ETag通常带双引号

}

// 处理CSS文件请求,启用协商缓存

app.get('/style.css', (req, res) => {

 const filePath = path.join(_dirname, 'public', 'style.css');

 const stats = fs.statSync(filePath); // 获取文件状态(含修改时间)

 const fileETag = getETag(filePath);

 const lastModified = stats.mtime.toUTCString(); // 格式化为HTTP时间

 // 1. 检查If-None-Match(ETag)

 if (req.headers['if-none-match'] === fileETag) {

   return res.status(304).end(); // 哈希一致,返回304

 }

 // 2. 检查If-Modified-Since(Last-Modified)

 if (req.headers['if-modified-since'] === lastModified) {

   return res.status(304).end(); // 时间未变,返回304

 }

 // 3. 资源已更新,返回新资源并设置协商缓存头

 res.setHeader('ETag', fileETag);

 res.setHeader('Last-Modified', lastModified);

 res.setHeader('Cache-Control', 'no-cache'); // 禁用强缓存,强制走协商缓存

 res.sendFile(filePath);

});

app.listen(port, () => {

 console.log(`Server running at http://localhost:${port}`);

});

运行该服务后,第一次访问http://localhost:3000/style.css会返回200 OK和 CSS 内容;刷新页面(强缓存已禁用),浏览器会携带If-None-MatchIf-Modified-Since请求,若 CSS 未修改,服务器返回304,浏览器复用本地缓存。

四、强缓存与协商缓存的核心差异

为了更清晰地对比两种缓存机制,我们整理了关键差异点:

对比维度强缓存协商缓存
是否发起请求未过期时不发起任何网络请求每次都会发起请求(但可能不返回内容)
判断逻辑客户端独立判断(基于缓存有效期)服务器判断(基于资源标识)
状态码未过期:200 (from cache)未修改:304;已修改:200
核心头Cache-Control、ExpiresETag/If-None-Match、Last-Modified/If-Modified-Since
适用场景长期不变的静态资源(如图片、第三方库)频繁更新的资源(如 HTML、业务 JS)

五、实际项目中的缓存策略建议

  1. 静态资源(CSS/JS/ 图片)
  • 启用强缓存,设置较长有效期(如 7-30 天);

  • 结合 “资源指纹”(如文件名加哈希:style.5f8d7a3e.css):当资源更新时,文件名哈希变化,浏览器会认为是新资源,自动绕过缓存加载新内容,避免 “缓存生效但资源已更新” 的问题。

  1. HTML 文件
  • 禁用强缓存(设置Cache-Control: no-cache),强制走协商缓存;

  • 原因:HTML 是页面入口,若 HTML 被强缓存,即使 CSS/JS 已更新,用户仍会加载旧 HTML,导致资源引用错误。

  1. 实时性资源(如接口数据)
  • 禁用缓存(设置Cache-Control: no-store),或使用协商缓存(基于数据更新时间);

  • 避免接口数据被缓存,导致用户看到旧数据。

总结

浏览器缓存是前端性能优化的 “基石”,而强缓存和协商缓存则是缓存机制的 “左右护法”—— 强缓存负责 “快速复用”,减少请求次数;协商缓存负责 “精准更新”,避免资源过期。在实际项目中,我们需要根据资源类型和更新频率,灵活组合两种缓存机制,才能在 “加载速度” 和 “资源新鲜度” 之间找到最佳平衡。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值