浏览器缓存机制详解
它们其实代表了浏览器在加载网页时,按照先后顺序依次检查的缓存防线。
下面我们将按照这 6 种缓存机制进行梳理,详细讲解它们是什么、何时缓存以及如何清除:
一、浏览器缓存的 6 道防线(按请求命中顺序)
当浏览器发起一个请求时,它会按照以下顺序依次寻找缓存(前面的没命中,才会找下一个):
① DNS Cache(DNS 缓存)
- 是什么 :网络请求的第一步是将域名(如
www.baidu.com)解析为服务器的 IP 地址。这个解析过程是耗时的,因此浏览器、操作系统、本地路由器等都会缓存解析结果。 - 何时缓存:浏览器第一次成功解析域名后,会自动将其 IP 缓存在本地一段时间。
- 如何清除 :
- 代码侧 :前端代码无法 直接控制或清除 DNS 缓存。部分 HTML 标签可用于预解析 以加快速度(
<link rel="dns-prefetch" href="//example.com">),但不能清除现有缓存。 - 用户侧 :在 Chrome 浏览器地址栏输入
chrome://net-internals/#dns,点击 "Clear host cache" 可以手动清除。或者在操作系统中刷新(如 Windows cmd 执行ipconfig /flushdns)。
- 代码侧 :前端代码无法 直接控制或清除 DNS 缓存。部分 HTML 标签可用于预解析 以加快速度(
② Service Worker Cache(离线缓存)
- 是什么 :如果域名解析完成,请求发出去前,会被 Service Worker(独立于主线程的后台脚本)拦截。这是唯一一个完全由开发者控制的缓存机制,是实现 PWA(渐进式 Web 应用)离线访问的核心。
- 何时缓存 :完全由开发者编写的 JavaScript 代码决定。开发者可以通过
Cache API手动拦截请求,将响应存入 Cache Storage。如果 Service Worker 中没命中缓存,请求才会继续往后走。 - 如何清除 :
- 代码侧 :通过 JS 代码调用
caches.delete('cache-name')或在 Service Worker 更新时主动注销/清理旧版本缓存。 - 用户侧 :在 DevTools (F12) -> Application -> Service Workers 中点击
Unregister,或在 Cache Storage 面板中手动右键删除。
- 代码侧 :通过 JS 代码调用
③ Memory Cache(内存缓存)
- 是什么:存储在计算机内存(RAM)中的缓存。如果 Service Worker 没命中,浏览器会检查内存缓存。它的读取速度最快,但生命周期最短。
- 何时缓存 :浏览器自动 决定。它主要缓存当前页面已经下载完毕的资源(如图片、CSS、JS)。甚至如果你在页面里动态创建了一个
<img>标签,它也会进内存缓存。 - 特点 :短期缓存 。一旦你关闭了这个 Tab 页,内存缓存就会被完全释放。在控制台的 Network 面板,你会看到
(from memory cache)。 - 如何清除 :
- 代码侧:前端代码无法精确控制或清除,由浏览器内核自动管理。
- 用户侧 :直接关闭当前的浏览器标签页(Tab)即可释放;或者在 Network 面板勾选
Disable cache(停用缓存)。
④ Disk Cache(磁盘缓存)
- 是什么:存储在计算机硬盘(硬盘/SSD)上的缓存。它的读取速度比内存慢,但容量大,且生命周期长(跨页面、甚至跨浏览器重启)。
- 何时缓存 :浏览器根据 HTTP 缓存规则(强缓存/协商缓存) 自动将资源写入硬盘。通常较大的文件、明确设置了可以长期缓存的文件会被放入这里。
- 特点 :长期缓存 。控制台的 Network 面板中会显示
(from disk cache)。 - 如何清除:它的管理和清除规则,完全遵循下文的 HTTP Cache 机制。
⑤ HTTP Cache(HTTP 缓存机制)
- 是什么 :严格来说,Disk Cache 是物理存储位置,而 HTTP Cache 是判断缓存是否有效的规则协议 。它分为强缓存 和协商缓存 。当浏览器检查硬盘(Disk Cache)时,就是依据 HTTP 规则来判断的。
- 强缓存 :浏览器检查响应头中的
Cache-Control(如max-age=3600) 或Expires。如果没过期就直接用本地硬盘的,根本不向服务器发请求。 - 协商缓存 :如果强缓存过期了,或者设置为
no-cache,浏览器会向服务器发一次请求 ,带上If-Modified-Since(对应服务器之前的Last-Modified) 或If-None-Match(对应服务器之前的ETag)。服务器比对后,没变化就返回304 Not Modified,让浏览器继续用本地缓存;变化了就返回200和新资源。
- 强缓存 :浏览器检查响应头中的
- 如何清除 / 开发者如何避免缓存死角 :
- 代码侧 (避免缓存) :
- 静态资源加 Hash (现代主流) :Webpack/Vite 等构建工具打包时给文件名加哈希(如
app.12345.js),文件内容一变 Hash 就变,浏览器找不到旧哈希文件,就不得不请求新文件。 - 设置不缓存请求头 :服务器端(Nginx 等)给 HTML 入口文件设置不缓存:
Cache-Control: no-cache, no-store, must-revalidate。 - Ajax/Fetch 请求加时间戳/随机数 :
fetch('/api/data?t=' + Date.now()),强制每次请求 URL 都不一样。
- 静态资源加 Hash (现代主流) :Webpack/Vite 等构建工具打包时给文件名加哈希(如
- 用户侧 :
- 强制刷新(Ctrl + F5 / Cmd + Shift + R)。
- 开发者工具中勾选
Disable cache。 - 浏览器设置中"清除浏览数据" -> 清除"缓存的图片和文件"。
- 代码侧 (避免缓存) :
⑥ BFCache(Back/Forward Cache,往返缓存)
- 是什么:这是一种极其特殊的缓存机制,专门针对浏览器的**"前进"和"后退"按钮**优化。当用户点击后退时,浏览器不是重新加载页面,而是直接将整个页面在内存中的状态(包括 JavaScript 执行状态、DOM 树结构)"瞬间"完整恢复出来。
- 何时缓存:当用户离开当前页面(前往新页面)时,浏览器会自动尝试将当前页面存入 BFCache。
- 如何清除/控制 :
- 代码侧 :如果你不希望页面被 BFCache 缓存(例如涉及敏感的实时金融数据),可以通过设置响应头
Cache-Control: no-store来彻底禁用它。此外,页面上如果存在未关闭的 WebSocket 连接或 IndexedDB 事务,也会阻止浏览器使用 BFCache。前端可以通过监听pageshow和pagehide事件中的persisted属性来感知页面是从 BFCache 恢复的还是重新加载的。
- 代码侧 :如果你不希望页面被 BFCache 缓存(例如涉及敏感的实时金融数据),可以通过设置响应头
总结
你看到的这 6 种分类是非常标准的浏览器底层架构视角。它涵盖了从你输入 URL 敲击回车,到页面最终呈现的全链路缓存防线:
- 先查有没有 DNS 缓存。
- 请求发出去前,看有没有被 Service Worker 缓存 拦截。
- 接着去最快的 Memory Cache (内存) 里找。
- 内存没有,再去 Disk Cache (硬盘) 里找。
- 在找硬盘缓存时,严格遵守 HTTP Cache (强缓存/协商缓存) 的协议规则。
- 如果你点击了后退按钮,直接从特殊的 BFCache 瞬间恢复整个页面。