2553 字
13 分钟
浏览器缓存
一、为什么需要浏览器缓存?
1.1 什么是浏览器缓存?
浏览器在首次请求网络资源后,将资源(如图片、JS、CSS等)存储在本地设备的机制
1.2 缓存的核心价值
NOTE
- 提升加载速度,减少等待时间
- 减少重复请求,减小服务器压力
- 节省网络带宽消耗,减少数据传输
二、缓存类型
2.1 按存储位置划分
缓存类型 | 定义 | 特点 | 适用资源 |
---|---|---|---|
Memory Cache(内存缓存) | 存储在浏览器内存中的临时缓存 | 速度快;生命周期短(关闭标签页 / 浏览器即消失);容量有限(仅存近期刚加载、可能复用的资源) | 体积较小的图片(如页面顶部临时图标);刚加载完的 JS/CSS 脚本(如页面渲染临时脚本) |
Disk Cache(磁盘缓存) | 存储在本地磁盘中的持久化缓存,是强制缓存和协商缓存的主要存储容器,浏览器依 HTTP 头(如Cache-Control )管理有效期 | 速度中等(慢于内存、快于网络请求);生命周期长(关闭浏览器仍保留,需手动清除或过期);容量较大 | 网站通用静态资源(如首页 logo、全局 CSS、长期不变的 JS 库如 jQuery);体积较大的资源(如轮播图图片、视频封面) |
Service Worker Cache | 由「Service Worker」(浏览器后台独立线程,PWA 核心技术)管理的 “可编程缓存”,可自定义缓存逻辑 | 完全可控(代码决定缓存资源、时长、删除时机,不依赖浏览器默认规则);持久化(需代码手动清除);支持离线(实现网页离线访问) | PWA 核心资源(如离线页面 HTML、离线 JS/CSS、离线图标);需精准控制的资源(如电商商品详情页模板) |
Push Cache(推送缓存) | 浏览器缓存 “最后一道防线”,HTTP/2 协议中 “服务器推送资源” 的临时缓存,使用场景极窄 | 生命周期最短(仅存于当前会话,会话结束即消失);容量极小(仅缓存少量服务器推送资源);优先级最低(仅前三种缓存无资源时才检查) | 服务器通过 HTTP/2 推送的 “预加载资源”(如服务器预判用户访问页面,提前推送该页面的 CSS) |
2.2 按策略划分
缓存策略 | 核心逻辑 | 关键 HTTP 头 | 核心特点 | 适用场景 |
---|---|---|---|---|
强制缓存(强缓存) | 有效期内直接用本地缓存,不发送服务器请求 | 优先级:Cache-Control > ExpiresCache-Control :相对时间(如 max-age)Expires :绝对时间 | 零网络请求,快;有效期内不更新(需哈希解决) | 长期不变静态资源(如第三方 JS、logo) |
协商缓存(对比缓存) | 强缓存过期后发验证请求(带标识),服务器判有效返 304 用缓存,无效返 200 + 新资源 | 1. 时间戳组:If-Modified-Since / Last-Modified 2. 唯一标识组: If-None-Match / ETag | 解决更新问题;需发轻量验证请求 | 频繁更新需新鲜度资源(如新闻、商品页) |
启发式缓存 | 无缓存规则时,浏览器取 Date 与 Last-Modified 时差 10% 作为缓存有效期 | Date、Last-Modified(无则仅用 Date) | 兼容性兜底;不可控(浏览器有差异) | 无明确缓存规则的兜底场景 |
三、缓存核心机制:强缓存和协商缓存的双保险
四、如何正确设置缓存策略?
IMPORTANT最终目标:“高频资源快加载、更新资源能触达、源站压力降最低”
4.1 按资源类型制定策略
资源类型 | 核心特征 | 推荐缓存策略 | 具体配置示例(Cache-Control) | 原理说明 |
---|---|---|---|---|
静态不变资源 | 长期不更新、高频复用 | 强缓存 + 超长有效期 | max-age=31536000 (1 年) | 如第三方 JS 库(Vue/React)、logo、全局 CSS,用长强缓存最大化复用,更新靠版本控制(见第二部分)。 |
频繁更新静态资源 | 周期性更新、需新鲜度 | 协商缓存 + 短强缓存(降级) | max-age=3600, stale-while-revalidate=86400 | 如首页轮播图、活动 Banner:1 小时内用强缓存,过期后先返回缓存(保证速度),后台异步验证更新(保证新鲜度)。 |
HTML 页面 | 承载动态内容、需即时更新 | 协商缓存(禁用强缓存) | no-cache (或max-age=0, must-revalidate ) | HTML 是页面入口,若设强缓存,会导致整页无法更新;用no-cache 强制每次发验证请求,确保获取最新结构。 |
动态内容(API 接口) | 实时性高、个性化 | 禁用缓存 / 短时缓存(按需) | no-store (完全不缓存)或max-age=60 (1 分钟) | 如用户信息、商品实时价格:no-store 确保每次获取最新数据;非实时接口(如分类列表)可设 1 分钟缓存减少请求。 |
临时资源(验证码、临时图标) | 单次使用、时效性极短 | 禁用缓存 | no-store, no-cache | 避免缓存导致验证码重复、临时图标不刷新。 |
4.2 版本控制最佳实践
IMPORTANT
- 痛点:强缓存的最大痛点是 “有效期内资源更新无法触达浏览器”
- 原理:版本控制的本质是 “内容变则标识变”,让浏览器识别 “新资源” 并重新请求
- 关键原则:
- 版本标识仅用于静态资源(JS/CSS/ 图片),不用于 HTML(HTML 需协商缓存,确保能加载新资源路径)
- 哈希值需与 “资源内容” 绑定,而非 “构建时间”,避免内容未变但哈希变化(如仅改注释导致缓存失效)
4.2.1 文件名哈希(推荐)
- 原理:将资源内容的哈希值(如 MD5、SHA256)嵌入文件名,资源内容变更时,哈希值自动变化,文件名随之改变(如
logo.abc123.png
→logo.def456.png
)。- 优势:精准关联 “内容 - 标识”,仅更新变化的资源,不影响其他缓存(增量更新)。
- 实操示例:
- 构建工具(Webpack/Vite)自动实现:配置
output.filename: '[name].[contenthash].js'
,打包后生成index.8f2d7c.js
。- HTML 中引用新文件名:
<img src="/static/logo.abc123.png">
,资源更新后 HTML 引用logo.def456.png
,浏览器自动请求新资源。
4.2.2 路径版本化
- 原理:在资源路径中加入版本号(如
/v2/static/logo.png
),资源整体更新时,修改路径中的版本号(如/v3/static/logo.png
)。- 适用场景:资源批量更新(如网站大版本迭代),或无法修改文件名的场景(如老系统兼容)。
- 注意:避免频繁全量更新(如每次改一点都换版本号),会导致所有缓存失效,增加服务器压力。
4.3 避免缓存陷阱
CAUTION
陷阱类型 现象描述 根本原因 解决方案 HTML 过度缓存 页面更新后,用户仍看到旧页面(需强制刷新) 给 HTML 设置了 max-age=86400
(长强缓存)HTML 仅用协商缓存:设置 Cache-Control: no-cache
,确保每次验证是否有新 HTML(新 HTML 会引用新资源)。动态资源误设强缓存 用户看到旧的 API 数据(如旧商品价格) 给 API 接口(如 /api/goods
)设了max-age=3600
动态接口按需缓存:实时数据设 no-store
;非实时数据设短max-age
(如max-age=60
),并配合服务器端 “缓存 - Control” 头。跨环境缓存冲突 开发环境资源缓存到生产环境,导致样式错乱 开发 / 测试 / 生产环境共用同一域名,缓存键重复 1. 环境加路径前缀:开发 /dev/static/logo.png
,生产/prod/static/logo.png
;
2. 开发环境强制禁用缓存(Cache-Control: no-store
)。缓存未命中(伪缓存) 资源设了缓存,但浏览器仍频繁回源请求 1. 资源 URL 带随机参数(如 logo.png?t=123
);2. 缓存头配置错误(如漏写Cache-Control
)1. 避免 URL 随机参数(用哈希版本替代);
2. 确保所有静态资源返回正确Cache-Control
/Expires
头;
3. 检查服务器是否拦截缓存头(如 Nginx 配置覆盖)。ETag 重复导致协商失效 资源内容变了,但 ETag 不变,服务器返 304 ETag 基于 “文件大小 + 修改时间” 生成(而非内容哈希),内容变但大小 / 时间未变 1. 服务器配置 ETag 为 “内容哈希”(如 Nginx 启用 etag on;
并确保基于内容计算);
2. 优先用Last-Modified
(若资源修改时间精准)。
五、附录
5.1 常见HTTP缓存头部速查表
响应头
头部名称 | 核心作用 | 关键值 / 指令 | 典型场景 |
---|---|---|---|
Cache-Control | 核心缓存控制(HTTP/1.1) | - max-age=N(秒,强缓存期)- no-cache(需协商缓存)- no-store(不缓存)- public/private(缓存范围) | 带哈希 JS/CSS:public, max-age=31536000 ;HTML:no-cache |
Expires | 强缓存过期时间(HTTP/1.0) | 绝对时间(如Wed, 20 Jul 2025 12:00:00 GMT ) | 兼容旧环境,优先级低于 Cache-Control |
ETag | 协商缓存唯一标识 | 资源哈希(如"abc123" ) | 需精准验证的资源(如 HTML、更新图片) |
Last-Modified | 协商缓存最后修改时间 | 绝对时间(如Tue, 10 Jun 2024 08:30:00 GMT ) | 静态资源,优先级低于 ETag |
请求头
头部名称 | 核心作用 | 关键值来源 | 触发逻辑 |
---|---|---|---|
If-None-Match | 验证 ETag 是否失效 | 本地缓存的 ETag 值 | 服务器对比:一致返 304,否则返 200 + 新资源 |
If-Modified-Since | 验证 Last-Modified 是否失效 | 本地缓存的 Last-Modified 值 | 服务器对比:资源未改返 304,否则返 200 |
Pragma | 兼容旧版缓存控制 | 仅no-cache 常用 | 同Cache-Control: no-cache ,优先级最低 |
5.2 常见HTTP缓存相关状态码
状态码 | 核心含义 | 缓存场景说明 | 关键触发条件 |
---|---|---|---|
200 OK | 资源请求成功 | 1. 强缓存命中:响应头带from memory cache /from disk cache ,不回源2. 无缓存 / 缓存过期:回源获取新资源 | 1. 资源在max-age 内,或未过期2. 本地无缓存,或缓存失效且服务器返回新资源 |
304 Not Modified | 协商缓存命中,资源未更新 | 不返回资源体,浏览器复用本地缓存,仅传输响应头 | 请求携带If-None-Match /If-Modified-Since ,服务器判断资源未修改 |
206 Partial Content | 部分资源请求成功 | 复用本地缓存的部分资源(如大文件断点续传),仅回源缺失片段 | 请求带Range 头(指定资源片段),且本地缓存有对应片段 |