作者:Priyen Mehta PS 本人在转载基础上翻译和润色(你懂的,尊重原创,仅学习参考请勿商用)
若觉得文章太长,可直接看思维导图

🌟 写在前面
当我们谈论前端性能优化时,往往会忽略最具影响力的那个——缓存策略。本文将深入探讨前端缓存的各个层面,帮助你构建真正高性能的 Web 应用。
当你问前端开发者如何提升性能时,得到的答案通常是:
- "用懒加载啊…"
- "优化图片…"
- "使用 React.memo…"
- "做代码分割…"
这些建议都没错,也确实重要。但说实话,几乎没有哪种优化能像合理的缓存策略那样,给你带来如此显著的性能提升。
缓存是前端性能优化中杠杆效应最强的工具,却也是最容易被误解和忽视的。很多开发者认为缓存是"后端团队的事",结果就是 90% 的前端应用发起了远超必要数量的网络请求。
在这篇文章中,我们将深入探讨前端缓存的真正威力,如何正确使用它,以及它如何帮你节省带宽、时间、成本,并大幅提升用户体验。

💡 为什么前端缓存如此被低估?
大多数前端工程师容易忽略一个关键事实:
在第一次 API 调用之前节省的每一毫秒 = 用户感知性能的指数级提升
想想这些常见的用户操作:
- 重新打开页面
- 切换标签页
- 刷新信息流
- 在不同路由间导航
如果你的前端每次都要向后端请求相同且未变化的数据,那你就在白白浪费:
- ⏱️ 用户的时间
- 💰 后端的成本
- 📱 移动数据流量
- 🔋 设备电量
- ⚙️ CPU 周期
而这一切,其实都是不必要的。
前端缓存能够优雅地解决这个问题——而且几乎是即时生效。
1️⃣ 浏览器缓存 — 最容易被忽视的性能加速器
浏览器本身就为我们提供了强大的缓存层,可以缓存:
- 静态资源
- HTML 文档
- JavaScript 包
- 字体文件
- 图片资源
但魔法只有在服务器设置了正确的响应头时才会发生。
🚀 Cache-Control 示例
Cache-Control: public, max-age=31536000, immutable这个响应头告诉浏览器:
- "将这个资源存储 1 年"
- "除非文件名改变,否则不要重新下载"
- "可以安全地重复使用这个版本"
这就是为什么像 YouTube、Meta、Twitter 这样的网站在首次访问后能够瞬间加载。
📚 参考资料:
⚠️ 何时不应该使用缓存
对于 HTML 文档,始终保持不缓存:
Cache-Control: no-store这确保新的部署不会意外地提供过时的 UI。
2️⃣ 内存缓存 — 你最快的"应用内 RAM"
这种类型的缓存发生在你的应用内部(Vue、React、Svelte 等)。
一旦你将 API 结果存储在内存中,它们就可以跨重渲染、跨导航使用,有时甚至可以跨组件共享。
示例:Vue 3 内存缓存实现
// cacheService.ts
interface CacheData {
[key: string]: any;
}
const memoryCache: CacheData = {};
/**
* 带缓存的数据获取函数
* @param url - 请求地址
* @returns Promise<any> - 返回缓存或新获取的数据
*/
export async function fetchWithCache(url: string): Promise<any> {
// 如果缓存中存在,直接返回
if (memoryCache[url]) {
console.log('✅ 从内存缓存中读取:', url);
return memoryCache[url];
}
// 否则发起请求
const response = await fetch(url);
const data = await response.json();
// 存入缓存
memoryCache[url] = data;
return data;
}在 Vue 3 组件中使用:
<script setup lang="ts">
import { ref, onMounted } from 'vue';
import { fetchWithCache } from '@/services/cacheService';
interface UserProfile {
id: number;
name: string;
email: string;
}
const userProfile = ref<UserProfile | null>(null);
const loading = ref(false);
onMounted(async () => {
loading.value = true;
try {
// 第二次加载时将从缓存读取,耗时接近 0ms
userProfile.value = await fetchWithCache('/api/user/profile');
} finally {
loading.value = false;
}
});
</script>
<template>
<div class="user-profile">
<div v-if="loading" class="loading">加载中...</div>
<div v-else-if="userProfile" class="profile-content">
<h2>{{ userProfile.name }}</h2>
<p>{{ userProfile.email }}</p>
</div>
</div>
</template>突然间,第二次加载变成了:
- ⚡ 0ms 延迟
- 🚫 无网络请求
- ✨ 无需等待
你会惊讶于有多少应用没有这样做。
3️⃣ LocalStorage / SessionStorage — 持久化缓存
这种缓存方式适用于以下场景:
- 数据很少变化
- 用户频繁访问
- 需要离线行为
- 应用重载时不应触发 API 调用
📚 参考资料:
⭐ 示例:缓存 API 响应 24 小时
// storageCache.ts
interface CachedData<T> {
timestamp: number;
data: T;
}
const CACHE_KEY = 'dashboard-stats';
const EXPIRE_TIME = 24 * 60 * 60 * 1000; // 24 小时
/**
* 获取仪表盘统计数据(带 24 小时缓存)
* @returns Promise<DashboardStats> - 统计数据
*/
export async function getDashboardStats<T>(): Promise<T> {
try {
// 尝试从 localStorage 读取缓存
const cachedString = localStorage.getItem(CACHE_KEY);
if (cachedString) {
const cached: CachedData<T> = JSON.parse(cachedString);
const isExpired = Date.now() - cached.timestamp > EXPIRE_TIME;
// 如果未过期,直接返回缓存数据
if (!isExpired) {
console.log('✅ 从 localStorage 读取缓存');
return cached.data;
}
}
} catch (error) {
console.warn('读取缓存失败:', error);
}
// 缓存不存在或已过期,发起新请求
console.log('🌐 发起新的 API 请求');
const response = await fetch('/api/dashboard/stats');
const data: T = await response.json();
// 存储到 localStorage
const cacheData: CachedData<T> = {
timestamp: Date.now(),
data
};
localStorage.setItem(CACHE_KEY, JSON.stringify(cacheData));
return data;
}在 Vue 3 中使用:
<script setup lang="ts">
import { ref, onMounted } from 'vue';
import { getDashboardStats } from '@/services/storageCache';
interface DashboardStats {
totalUsers: number;
activeUsers: number;
revenue: number;
}
const stats = ref<DashboardStats | null>(null);
onMounted(async () => {
// 即使刷新页面,UI 也能立即加载
stats.value = await getDashboardStats<DashboardStats>();
});
</script>
<template>
<div class="dashboard">
<div v-if="stats" class="stats-grid">
<div class="stat-card">
<h3>总用户数</h3>
<p class="stat-value">{{ stats.totalUsers.toLocaleString() }}</p>
</div>
<div class="stat-card">
<h3>活跃用户</h3>
<p class="stat-value">{{ stats.activeUsers.toLocaleString() }}</p>
</div>
<div class="stat-card">
<h3>收入</h3>
<p class="stat-value">¥{{ stats.revenue.toLocaleString() }}</p>
</div>
</div>
</div>
</template>即使在页面刷新后,UI 也能瞬间加载。
4️⃣ Service Workers — 浏览器的超能力缓存 API
如果你想要最高级别的控制,Service Workers 就是王者。
你可以获得:
- 🔌 离线模式
- 🔄 Stale-While-Revalidate 策略
- ⚡ 优化的预取
- 🔄 后台同步
- 🎯 完全的 Cache API 控制
📚 参考资料:
示例:使用 Service Worker 的 Cache API
// service-worker.js
const CACHE_VERSION = 'v1';
const CACHE_NAME = `app-cache-${CACHE_VERSION}`;
// 监听 fetch 事件
self.addEventListener('fetch', (event) => {
event.respondWith(
// 先尝试从缓存中获取
caches.match(event.request).then((cachedResponse) => {
// 同时发起网络请求
const networkFetch = fetch(event.request)
.then((response) => {
// 将新响应存入缓存
caches.open(CACHE_NAME).then((cache) => {
cache.put(event.request, response.clone());
});
return response;
})
.catch(() => {
// 网络失败时,返回缓存的响应
console.log('网络请求失败,使用缓存');
});
// 如果有缓存,立即返回;否则等待网络请求
return cachedResponse || networkFetch;
})
);
});
// 清理旧缓存
self.addEventListener('activate', (event) => {
event.waitUntil(
caches.keys().then((cacheNames) => {
return Promise.all(
cacheNames
.filter((name) => name !== CACHE_NAME)
.map((name) => caches.delete(name))
);
})
);
});这使你的应用:
- ⚡ 极快的响应速度
- 🎯 极高的一致性
- 📶 完美适配弱网环境
如果你正在构建现代 PWA(渐进式 Web 应用)—— 务必使用这个。
5️⃣ HTTP 级别缓存:ETags + Stale-While-Revalidate
大多数前端应用在这方面做得不够好:
它们获取数据 → 后端发送新数据 → 浏览器每次都重新下载。
但通过 ETags + SWR(Stale-While-Revalidate),浏览器和后端可以进行智能通信:
工作流程:
- 浏览器: "这是我已有数据的 ETag,它有变化吗?"
- 后端: "没有变化 → 返回 304 Not Modified"
- 浏览器: 立即使用缓存的响应
- 仅在变化时 → 后端发送新的数据负载
📚 参考资料:
结果:
- 🔥 加载速度提升 10 倍
- 🔥 带宽减少 70-95%
- 🔥 后端负载大幅下降
示例响应头:
Cache-Control: max-age=0, must-revalidate
ETag: "abc123def456"在 Vue 3 中配合 Axios 使用:
// apiClient.ts
import axios from 'axios';
const apiClient = axios.create({
baseURL: '/api',
// 启用 ETag 支持
headers: {
'Cache-Control': 'max-age=0'
}
});
// 自动处理 ETag
apiClient.interceptors.response.use(
(response) => {
// 存储 ETag
if (response.headers.etag) {
const cacheKey = response.config.url;
sessionStorage.setItem(`etag:${cacheKey}`, response.headers.etag);
}
return response;
},
(error) => {
// 304 响应被视为成功
if (error.response?.status === 304) {
console.log('✅ 数据未变化,使用缓存');
}
return Promise.reject(error);
}
);6️⃣ 框架级缓存:VueUse、TanStack Query
现代前端框架已经内置了强大的缓存功能。
➤ Vue 3 + VueUse
VueUse 提供了 useFetch 和 useAsyncState 等组合式函数,内置缓存支持:
<script setup lang="ts">
import { useFetch } from '@vueuse/core';
interface TodoItem {
id: number;
title: string;
completed: boolean;
}
// 自动缓存,避免重复请求
const { data: todos, isFetching, error } = useFetch<TodoItem[]>(
'/api/todos',
{
// 5 分钟内不重新请求
refetch: false,
initialData: [],
}
).json();
</script>
<template>
<div class="todo-list">
<div v-if="isFetching" class="loading">加载中...</div>
<div v-else-if="error" class="error">{{ error }}</div>
<ul v-else>
<li v-for="todo in todos" :key="todo.id">
{{ todo.title }}
</li>
</ul>
</div>
</template>📚 参考资料:
➤ Vue 3 + TanStack Query (Vue Query)
<script setup lang="ts">
import { useQuery } from '@tanstack/vue-query';
interface TodoItem {
id: number;
title: string;
completed: boolean;
}
const fetchTodos = async (): Promise<TodoItem[]> => {
const response = await fetch('/api/todos');
return response.json();
};
// 智能缓存,5 分钟内数据被视为"新鲜"
const { data: todos, isLoading, error } = useQuery({
queryKey: ['todos'],
queryFn: fetchTodos,
staleTime: 1000 * 60 * 5, // 5 分钟
// 后台自动重新验证
refetchOnWindowFocus: true,
});
</script>
<template>
<div class="todo-list">
<div v-if="isLoading">加载中...</div>
<div v-else-if="error">出错了:{{ error.message }}</div>
<ul v-else>
<li v-for="todo in todos" :key="todo.id">
<input type="checkbox" :checked="todo.completed" />
<span>{{ todo.title }}</span>
</li>
</ul>
</div>
</template>📚 参考资料:
这创造了:
- 🧠 智能缓存
- 🔄 后台同步
- ⏰ 自动过期失效
- ✨ 乐观 UI 更新
太多应用在每次路由加载时都重新获取所有数据——这优雅地解决了这个问题。
7️⃣ 预取、预加载与预渲染 — 隐形缓存
Google、Amazon、Netflix —— 都在使用这个技巧。
Prefetch:"用户可能很快会点击这个"
<link rel="prefetch" href="/next-page.js">📚 参考资料:
Preload:"现在就加载这个,因为它很关键"
<link rel="preload" href="hero-image.webp" as="image">📚 参考资料:
Prerender:"隐形加载整个下一页"
<link rel="prerender" href="/checkout">在 Vue 3 中动态添加预取:
<script setup lang="ts">
import { onMounted } from 'vue';
onMounted(() => {
// 预取可能访问的页面
const link = document.createElement('link');
link.rel = 'prefetch';
link.href = '/dashboard';
document.head.appendChild(link);
});
</script>这使得下一次导航感觉瞬间完成。
8️⃣ CDN 缓存 — 最强大的缓存层
你的 CDN(Cloudflare、Fastly、Akamai、Vercel)允许:
- 🌍 边缘缓存
- 🔄 边缘的 Stale-While-Revalidate
- 🎯 动态回退缓存
- 🌐 地理分布式读取副本
示例:
Cache-Control: public, max-age=60, stale-while-revalidate=120用户即使在获取新数据时也能获得即时响应。
公司通过正确的 CDN 配置可以节省数百万的基础设施成本。
📚 参考资料:

9️⃣ 真实案例:展示其影响力
✔ 案例 1 — 仪表盘加载速度提升 5 倍
一个仪表盘在每次路由变化时都会刷新。添加内存 + 存储缓存后,网络调用减少了 83%,加载时间从 1.8s → 350ms。
✔ 案例 2 — 移动应用节省 40% 用户带宽
LocalStorage 缓存 + ETag 验证避免了重复下载列表数据。
✔ 案例 3 — 前端安全的重大胜利
不当的缓存可能会暴露敏感数据(例如,公开缓存的认证页面)。
合理的缓存策略配合正确的 Cache-Control 头可以避免此类安全问题:
# 私密数据,不应被缓存
Cache-Control: private, no-cache, no-store, must-revalidate🎯 总结
前端缓存不仅仅是"一种优化"。
它是前端开发者可用的最强大、最具影响力、最未被充分利用的性能策略之一。
正确实施缓存可以:
- ⚡ 将加载时间减少 70-90%
- 📉 显著减少后端调用
- ✨ 让应用感觉瞬间响应
- 📱 节省用户带宽
- 🔌 改善离线体验
- 💰 降低服务器账单
- 🔒 增强安全性(如果谨慎实施)
下次你计划性能优化冲刺时,不要从包大小和 lint 规则开始。
从这个问题开始:
"我们可以缓存什么?"
因为在前端性能中,缓存不是事后的想法——它是一种超能力 💪✨
📚 延伸阅读
希望这篇文章对你有所帮助!如果你觉得有价值,欢迎分享给更多的开发者朋友 🤝
- 本文链接:https://fridolph.top/posts/2025-06-28__fe-cache
- 版权声明:本博客所有文章除特别声明外,均默认采用 CC BY-NC-SA 许可协议。