摘要
随着前端项目体积越来越大、用户体验要求越来越高,“快”成为了产品竞争的关键指标之一。为了减少重复请求、降低服务器压力、提升用户访问速度,前端缓存策略逐渐成为前端开发中不可忽视的一部分。
这篇文章将从几个常见缓存策略出发,通过代码实例和实际场景,介绍如何在前端开发中落地高效的缓存方案。
引言
在传统的网页开发中,服务器每次都要响应完整的请求和资源数据,这种方式不仅资源浪费,还可能让页面加载变得缓慢。现在前端已经能处理更多逻辑,通过结合HTTP 缓存、本地存储、IndexedDB、Service Worker等手段,我们可以实现“智能缓存”,把访问速度提升一个量级。
适用场景包括:
- 静态资源缓存(JS/CSS 图片)
- 用户数据缓存
- 离线访问能力(PWA)
- 数据结构化存储
接下来我们一个个来看。
浏览器缓存控制(HTTP Cache-Control)
设置资源的缓存时间,让浏览器自动帮我们记住内容
浏览器缓存最简单的做法就是通过 HTTP 头设置资源的缓存策略。比如我们在返回 JS 或 CSS 的时候加上如下响应头:
Cache-Control: max-age=31536000, immutable
意思是这个资源在未来 一年内 浏览器都不需要重新拉取。再结合前端构建工具(如 Webpack/Vite)的 hash 输出,我们就能避免更新被缓存住的问题。
<!-- 自动带上 hash 的 JS 文件,修改后文件名会变 -->
<script src="/assets/main.4ad93a.js"></script>
应用场景举例
适合缓存长期不变的:
- Vue/React 构建产物
- 第三方库(如 Element、Bootstrap)
- 图标或字体文件
本地存储(localStorage)
可以存储一些轻量级的数据,比如用户信息、配置项等
我们可以利用浏览器的 localStorage 保存 JSON 格式的数据,页面加载时优先读取缓存,没有再请求接口。
示例代码
const cacheKey = 'userInfo';
const cache = localStorage.getItem(cacheKey);
if (cache) {
render(JSON.parse(cache)); // 如果有缓存,直接用
} else {
fetch('/api/user')
.then(res => res.json())
.then(data => {
localStorage.setItem(cacheKey, JSON.stringify(data));
render(data);
});
}
应用场景举例
- 用户登录后的基本信息(头像、昵称)
- 页面配置项(是否显示某个弹窗、默认主题色)
- 新闻列表页的上次浏览状态
使用 IndexedDB 存储结构化数据
用来处理复杂对象或较大的数据,支持异步读写,是浏览器内建数据库
localStorage 只能存字符串,而且有大小限制(大约 5MB),这时我们可以使用 IndexedDB 来存储更复杂的数据。
示例代码
const request = indexedDB.open('AppDB', 1);
// 初始化数据库
request.onupgradeneeded = event => {
const db = event.target.result;
db.createObjectStore('users', { keyPath: 'id' });
};
request.onsuccess = event => {
const db = event.target.result;
const tx = db.transaction('users', 'readonly');
const store = tx.objectStore('users');
const getUser = store.get('u001');
getUser.onsuccess = () => {
const user = getUser.result;
console.log('User from cache:', user);
};
};
应用场景举例
- 聊天记录缓存(不想每次打开都重新加载)
- 离线购物车数据
- 存储复杂对象(如图书信息、字典表)
Service Worker 实现离线缓存
Service Worker 是浏览器提供的一种可拦截请求的后台线程,适合实现 PWA 离线体验
可以用它在用户访问时把页面和资源缓存起来,下次即便断网也能正常访问。
示例代码(简化)
// service-worker.js
self.addEventListener('install', event => {
event.waitUntil(
caches.open('v1').then(cache => {
return cache.addAll([
'/',
'/index.html',
'/style.css',
'/main.js'
]);
})
);
});
self.addEventListener('fetch', event => {
event.respondWith(
caches.match(event.request)
.then(resp => resp || fetch(event.request))
);
});
注册方式:
// main.js
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/service-worker.js');
}
应用场景举例
- 移动端新闻阅读应用
- 图片浏览类 App(提前缓存图片)
- 表单应用(断网提交失败可以暂存)
常见问答 QA 环节
Q1:localStorage 和 IndexedDB 该怎么选?
- 数据小且简单:
localStorage - 数据大或需要搜索、结构化存储:
IndexedDB
Q2:浏览器缓存为什么要带 hash?
因为浏览器不知道你文件改没改。有 hash 就能确保只缓存没变的文件,改了就重新拉取。
Q3:Service Worker 会立即生效吗?
不会。默认情况下注册后要等旧的 service worker 被替换才生效。可以手动调用 skipWaiting() 提前激活。
总结
缓存策略不是单选题,而是要根据业务场景组合使用:
| 场景 | 推荐策略 |
|---|---|
| JS/CSS 图标等静态资源 | HTTP 缓存 + hash |
| 用户信息/配置项 | localStorage |
| 离线大数据 | IndexedDB |
| 离线访问体验 | Service Worker |
做前端不光是“写页面”,合理的缓存设计也是“写好页面”的一部分。如果你在项目中遇到缓存问题或不确定该用哪种方式,欢迎留言一起讨论,我们可以再深入优化一套策略方案。