HTTP缓存从强缓存到协商缓存,前端性能优化第一步

页面加载慢,第一反应是优化代码、压缩资源。但还有一个更容易忽略的点------HTTP缓存。

配置得当的话,二次访问几乎不用发请求,资源直接从本地拿。今天把HTTP缓存的完整机制讲清楚。

强缓存:直接从本地拿,不问服务器

强缓存的意思是:浏览器第一次请求资源后,服务器会告诉浏览器"这个资源你可以缓存多久"。在这个时间内,浏览器再次需要这个资源时,直接从本地读取,不发任何请求。

控制强缓存的两个HTTP头:

HTTP头 出现版本 优先级 说明
Cache-Control HTTP/1.1 高 max-age=3600表示缓存3600秒
Expires HTTP/1.0 低 绝对时间,如Wed, 21 Oct 2026 07:28:00 GMT

两个都存在时,Cache-Control优先。

yaml 复制代码
HTTP/1.1 200 OK
Cache-Control: max-age=31536000
Expires: Thu, 10 Oct 2027 00:00:00 GMT
Content-Type: application/javascript

// JS文件内容...

常见的Cache-Control指令:

csharp 复制代码
max-age=3600        # 缓存3600秒
no-cache            # 不用强缓存,每次都走协商缓存
no-store            # 完全不缓存
public              # 中间代理(CDN)也可以缓存
private             # 只有浏览器可以缓存,CDN不行

协商缓存:缓存过期了,问一嘴服务器

强缓存过期后,浏览器不会直接重新下载,而是先问服务器:"这个资源有没有更新?"

这就是协商缓存,通过两个请求头实现:

方案一:Last-Modified / If-Modified-Since

yaml 复制代码
# 第一次请求,服务器返回:
Last-Modified: Wed, 09 Oct 2026 12:00:00 GMT

# 缓存过期后,浏览器带上:
If-Modified-Since: Wed, 09 Oct 2026 12:00:00 GMT

# 服务器比较时间:
# 没改过 → 304 Not Modified(用本地缓存)
# 改过了 → 200 OK + 新内容

方案二:ETag / If-None-Match(推荐)

python 复制代码
# 第一次请求,服务器返回:
ETag: "abc123def456"

# 缓存过期后,浏览器带上:
If-None-Match: "abc123def456"

# 服务器比较ETag值:
# 一样 → 304 Not Modified
# 不一样 → 200 OK + 新内容 + 新ETag

ETag比Last-Modified更精确------文件内容没变但修改时间变了(比如touch了一下),Last-Modified会误判为"已更新",ETag不会。

完整的缓存决策流程

sql 复制代码
浏览器请求资源
    ↓
检查强缓存(Cache-Control/Expires)
    ↓
  未过期 → 直接使用本地缓存(200 from cache)
    ↓
  已过期
    ↓
发起协商缓存请求(带ETag或Last-Modified)
    ↓
  服务器返回304 → 使用本地缓存
  服务器返回200 → 下载新资源

实际项目中的缓存策略

不同类型文件的缓存策略不一样:

资源类型 缓存策略 理由
HTML no-cache 入口文件,必须每次都验证
JS/CSS(带hash) max-age=31536000, immutable 文件名含hash,内容变了hash就变
JS/CSS(不带hash) no-cache 需要协商缓存确认版本
图片/字体 max-age=86400 缓存一天,平衡更新频率和性能
API响应 no-store 动态数据,不缓存

Webpack打包时给文件名加content hash是最佳实践:

javascript 复制代码
// webpack.config.js
output: {
  filename: '[name].[contenthash:8].js',
  chunkFilename: '[name].[contenthash:8].chunk.js'
}

这样HTML里引用的是app.a1b2c3d4.js,内容变了hash就变,浏览器会请求新文件。旧文件可以永久缓存,不用担心用户拿到旧版本。

开发者工具看缓存

Chrome DevTools的Network面板能看到每个资源的缓存状态:

  • (disk cache) --- 从磁盘缓存读取
  • (memory cache) --- 从内存缓存读取
  • (from service worker) --- 从SW读取
  • 304 --- 协商缓存命中,服务器说"没变"

调试时可以勾选"Disable cache"来跳过缓存,方便验证。


HTTP缓存是前端性能优化成本最低、收益最高的手段之一。配好了缓存策略,二次加载时间能砍掉一大半。

建议把自己项目的Network面板打开看看,有多少请求是304,有多少是真正下载的。

相关推荐
情 九6 小时前
MCP 实战 06:把 HTTP 接口接成工具,让 AI 帮你查数据
人工智能·网络协议·http·mcp
步行cgn17 小时前
HTTP 请求报文结构详解:请求行、请求头、请求体各自的作用
网络·网络协议·http
明天也要努力学习1 天前
HTTP 协议的基本格式和 fiddler 的用法
网络协议·http·fiddler
cpolar技术支持1 天前
网关加 gzip,同时开启 chunked,下载回来的包解不开:把两层编码的解压顺序与长度核对做成只读页
python·http·网络编程·socket·内网穿透
骑着蜗牛撵大象3272 天前
手写一个 HTTP 服务器:从 socket 到 epoll,Linux 上到底怎么把请求变成响应
linux·服务器·网络·http·epoll·socket编程·并发模型
沫璃染墨2 天前
从零入门计算机网络系列(十):HTTP协议进阶——从资源路径解析到状态码与请求头详解
linux·运维·服务器·网络·c++·http
|_⊙2 天前
应用层协议HTTP与HTTPS
网络·网络协议·http
路由侠内网穿透.2 天前
本地部署开源 WSGI HTTP 服务器 Gunicorn 并实现外部访问
运维·服务器·网络·网络协议·http·开源
川石课堂软件测试2 天前
学习软件测试需掌握的一些知识
数据库·python·sql·功能测试·网络协议·mysql·http
靠coding维持生计的potato3 天前
从HTTP到存储——文件上传的底层原理梳理
http