主要分为两种:强缓存,协商缓存
- 强缓存:浏览器请求资源时,通过
Cache-Control和Expires等响应头中的字段判断是否缓存命中,就从缓存中加载资源,不会向服务器发送请求 - 协商缓存:当强缓存未命中时,通过请求头中的
If-Modified-Since或If-None-Match字段来判断资源是否发生变化- 如果资源未变化,服务器返回
304 Not Modified状态码,浏览器继续使用缓存资源。 - 如果资源已变化,服务器返回
200 OK状态码和新资源。
- 如果资源未变化,服务器返回
实际工程中怎么用
核心思路
能用强缓存就用强缓存(零网络开销),但要解决”怎么更新”的问题。不能保证内容稳定的,用协商缓存兜底。
静态资源(JS / CSS / 图片)→ 强缓存 + 文件名哈希
这是现代前端项目最标准的做法。Webpack / Vite 打包时,会把文件内容的哈希值注入文件名:
main.a3f9c2d1.js
style.7b2e4f8a.css
然后服务器对这类资源设置超长强缓存:
Cache-Control: public, max-age=31536000, immutableimmutable告诉浏览器”这个文件永远不会变,连协商请求都不用发”- 文件变了?打包出来的文件名哈希就变了,浏览器当成全新 URL,自动拉最新
结果: 老用户二次访问,JS/CSS 全部命中强缓存,0 网络请求,页面秒开。
HTML 入口文件 → 协商缓存(no-cache)
HTML 是整个应用的入口,它引用了带哈希的 JS/CSS 文件名。HTML 本身必须每次都能拿到最新,否则用户永远加载旧版本的 JS。
Cache-Control: no-cache浏览器每次都会带着 If-None-Match 去问服务器:“HTML 变了吗?”
- 没变 → 返回 304,用本地缓存,几乎无开销
- 变了(新版上线)→ 返回新 HTML,新 HTML 里的 JS/CSS 文件名哈希也变了,浏览器自动拉新资源
千万不要给 HTML 设强缓存
一旦 HTML 被强缓存缓住,新版本上线后用户完全感知不到,只能等缓存自然过期,无法热修复。
CDN 上的资源 → 区分 public 和 private
# 静态资源,CDN 可以缓存,所有人共用
Cache-Control: public, max-age=31536000
# 用户个人数据(头像、账单),只能浏览器缓存,CDN 不能缓
Cache-Control: private, no-cachepublic 的意义在于:同一个资源,全球用户第一次请求后,CDN 就缓存了,后续用户直接从最近的 CDN 节点拿,源站压力大幅降低。
API 接口 → 视场景而定
| 接口类型 | 推荐策略 | 原因 |
|---|---|---|
| 实时数据(股票、消息) | no-store | 不允许任何缓存,每次都要最新数据 |
| 低频变动的配置类数据(城市列表、分类) | public, max-age=3600 | 数据几乎不变,强缓存 1 小时完全可以 |
| 用户个人信息 | private, no-cache | 私有数据,走协商缓存保证拿到最新 |
| POST / PUT / DELETE 请求 | 不缓存(浏览器默认不缓存) | 写操作天然不缓存 |
强制更新缓存的几种手段
上线新版但用户还在用旧缓存,怎么办?
- 文件名哈希(最优):让 Vite/Webpack 帮你自动处理,无需手动干预
- Query String(简单粗暴):
main.js?v=20240326,改v参数让浏览器认为是新 URL - Service Worker:可以完全接管缓存逻辑,实现精细化控制(适合 PWA)
- 强制刷新:用户
Ctrl+Shift+R会跳过强缓存,临时应急手段
决策流程
请求一个资源
│
├─ 是 HTML 入口?
│ └─ Cache-Control: no-cache(协商缓存)
│
├─ 是带哈希的静态资源(JS/CSS/图片)?
│ └─ Cache-Control: public, max-age=31536000, immutable(强缓存)
│
├─ 是 API?
│ ├─ 实时数据 → no-store
│ ├─ 配置类数据 → public, max-age=合适的秒数
│ └─ 用户私有数据 → private, no-cache
│
└─ 包含用户敏感信息?
└─ 一定要用 private,禁止 CDN 缓存
Cache-Control (HTTP/1.1 )是目前最常用的字段,通过设置相对时间来控制。
max-age=31536000: 表示资源在 31536000 秒(1年)内是新鲜的,直接用缓存。no-cache: 不要被名字骗了! 它不是“不缓存”,而是“缓存,但在使用前必须去服务器验证一下(走协商缓存)”。no-store: 真正的“不缓存”,任何时候都去服务器下载最新的。public/private:public表示 CDN 也可以缓存;private表示只有浏览器能缓存(比如存了用户敏感信息的页面)。
- ETag/If - Noe -Match :在访问文件的时候,比较上次访问文件给的ETag,即由第一次文件内容生成的哈希值,与第二次访问文件的If - None - Match进行对比,如果相同说明文件没变,返回状态码304,如果变了,则重新访问,返回200与新数据
- Last- Modified / If - Modified - Since:这是一个很粗糙的比较,是将上一次与这一次文件的文件最后修改时间,如果修改时间一样,说明没有修改,返回304,变了,则重新访问文件
区分强缓存协商缓存步骤
- Step 1: 检查
Cache-Control或Expires。- 如果未过期 强缓存命中,直接读取资源。
- Step 2: 如果过期了,带上
ETag或Last-Modified发起请求。- 服务器比对凭据。
- Step 3:
- 凭据一致 返回 304,浏览器从缓存读。
- 凭据不一致 返回 200,服务器传回新资源并更新缓存。