前端开发怎么做缓存策略?3 种方式高效提性能

2026-11-13 07:04:34 浏览数 (77)

前端性能优化里,缓存策略是最划算的一招:它不要求重写业务,只要把"已经拿过的数据或资源"在合适的层留一会儿,页面加载就能明显变快。常见做法分三层——HTTP 层的强缓存与协商缓存、Service Worker 的离线缓存、以及运行时的客户端缓存。这三类方案适用场景不同,叠加起来收益最大,而不是只盯其中一种。本文先给结论,再按这三种方式拆解,帮你按场景选对缓存策略,少写代码也能把加载速度提上去。

前端怎么做缓存策略?3 种方式提性能

先看结论

做前端缓存策略,先分清你要缓存的是"静态资源"还是"接口数据"。静态资源优先吃 HTTP 强缓存,配合协商缓存兜底;要离线可用或秒开,加一层 Service Worker;接口数据或计算结果就用 localStorage 加内存缓存,但必须设计好过期与失效。三种方式不是互斥的,生产里往往叠加使用。下面三个小节分别讲清配置要点和最容易踩的雷。

一、HTTP 缓存:强缓存与协商缓存

这是浏览器原生支持的缓存,不用写额外代码,只要在响应头里下指令。强缓存靠 Cache-Control: max-age 告诉浏览器在多少秒内直接用本地副本,连请求都不发;超过时间后进入协商缓存,靠 ETag 或 Last-Modified 问服务端"我这份还新吗",服务端回 304 就继续用旧的。想补 JavaScript 基础可以看 JavaScript 基础教程。

# 强缓存:一年内直接用,适合带哈希的文件名
Cache-Control: public, max-age=31536000, immutable

# 协商缓存:每次问服务端是否变化
ETag: "v1-abc123"
Cache-Control: no-cache

最大的坑是给会变的文件设了过长的强缓存却不改名,导致发布后用户一直看到旧版。正确做法是静态资源带上内容哈希文件名(如 app.a1b2c3.js),这样内容一变文件名就变,旧缓存不会误命中。HTML 入口文件则要保守,通常用 no-cache 让它每次协商,保证能拿到最新引用。

实际项目里,强缓存通常配合 CDN 一起用:CDN 边缘节点同样遵守 Cache-Control,把哈希文件缓存到离用户更近的地方,首屏资源基本秒回。但要注意,改了 max-age 之后,旧副本在过期前仍可能被命中,所以"内容变了就换文件名"才是根本解,而不是指望调短缓存时间。把静态资源、HTML 入口、CDN 三者一起规划,缓存策略才算真正落地。

二、Service Worker:离线与第二道缓存

当你的页面需要离线可用、或者二次打开要"秒开",Service Worker 是标准答案。它在浏览器背后跑一个独立线程,能拦截网络请求并决定是走网络还是走它管理的缓存。典型模式是"先缓存后网络":首次访问把关键资源存起来,之后即使断网也能渲染首屏。关于标签与安全渲染基础可参考 HTML 参考手册。

// service-worker.js:缓存应用壳,离线也能开
self.addEventListener('install', (e) => {
  e.waitUntil(caches.open('v1').then((c) => c.addAll(['/', '/app.js', '/style.css'])));
});

self.addEventListener('fetch', (e) => {
  e.respondWith(
    caches.match(e.request).then((hit) => hit || fetch(e.request))
  );
});

坑在于更新:Service Worker 改了不生效,是因为旧 worker 还控制着页面。必须在新 worker 的 activate 阶段调用 clients.claim() 并清理旧缓存,否则用户卡在旧版本。另外缓存体积要设上限,别把整个站点无脑塞进去拖慢更新。

Service Worker 还有作用域(scope)的限制:默认只能控制注册页面同目录及子目录的请求,想管更上层要加 Service-Worker-Allowed 响应头。缓存策略上,别把整个站点无脑塞进去,建议只缓存首屏关键资源和离线必须的页面,其余走网络优先或 stale-while-revalidate,既保证离线可用又不拖慢更新。缓存体积也要设上限并定期清理,避免旧版本残留占用空间。

三、运行时客户端缓存:localStorage 与内存

接口数据或昂贵的计算结果,适合在运行时缓存。最简单是 localStorage 存 JSON,带一个写入时间戳,读取时先判断是否过期;更短命、更敏感的数据放内存变量即可,关闭页面就消失。二者结合能明显减少重复请求。可延伸阅读 HTML 与 CSS 区别 来理解前端基础分层的思路。

// 带过期时间的 localStorage 缓存
function getCached(key, ttl = 60000) {
  const raw = localStorage.getItem(key);
  if (!raw) return null;
  const { value, ts } = JSON.parse(raw);
  if (Date.now() - ts > ttl) { localStorage.removeItem(key); return null; }
  return value;
}
function setCached(key, value) {
  localStorage.setItem(key, JSON.stringify({ value, ts: Date.now() }));
}

最容易犯的错是把不该持久化的数据塞进 localStorage,比如大数组或含敏感信息的对象,既占空间又有泄露风险。localStorage 单域名约 5MB 上限,且是同步读写,频繁操作会阻塞主线程,数据量大时要改用 IndexedDB 或只在内存里放。

运行时缓存最难的不是写入,而是失效。localStorage 带时间戳过期的写法能应付小数据,但遇到频繁变动的列表或大型对象就力不从心。这时候可以升级到 IndexedDB,它支持更大容量和异步读写,不阻塞主线程;也可以用内存缓存做热数据、IndexedDB 做冷数据分层。无论哪种,都要给用户一个"手动刷新缓存"的入口,避免脏数据卡住界面。

该用哪种前端缓存

缓存命中判断流程

总结

前端缓存策略的本质是"在离用户更近的地方存一份副本"。静态资源用 HTTP 强缓存配哈希文件名,要离线秒开加 Service Worker,接口数据用 localStorage 加内存并设过期。三种方式叠加最稳,但都要把"失效与更新"设计清楚,否则缓存反而变成发布事故。按本文的三层清单落地,不写多少业务代码也能把加载速度提上去。缓存策略没有银弹,核心是先想清楚"缓存什么、失效什么、更新什么"这三问,再决定用哪一层。上线前用无痕窗口实际验证一次强缓存与协商缓存是否按预期生效,往往能发现本地缓存把问题掩盖的情况;把缓存检查写进上线前检查单,能少踩很多回滚事故。

常见问题

Q:强缓存设了很久,发新版本用户看不到怎么办?

根因是文件名没变,浏览器一直命中旧缓存。解决办法是给静态资源加内容哈希文件名,内容一变文件名就变,旧 URL 自然失效;HTML 入口则保持每次协商,确保引用的是最新文件。

Q:Service Worker 更新后为什么不生效?

因为旧 worker 仍控制着已打开的页面。要在新 worker 的 activate 事件里清理旧缓存并调用 clients.claim() 接管页面,用户下次操作或刷新后才会切到新版。

Q:localStorage 能存多少?大对象放进去会怎样?

单域名通常约 5MB,且读写是同步的。存大数组会占满配额并阻塞主线程,敏感信息还有泄露风险。大数据量改用 IndexedDB,短命敏感数据只放内存。

延伸学习