资源缓存策略设计
资源缓存策略设计
资源缓存是提升应用性能的关键手段之一,通过合理利用浏览器缓存机制,减少重复请求和网络传输时间。缓存策略需要根据资源类型、更新频率以及业务需求进行针对性设计。
浏览器缓存机制
浏览器缓存主要分为强缓存和协商缓存两类:
- 强缓存:直接使用本地副本,不发送请求到服务器。通过
Cache-Control
和Expires
头控制。 - 协商缓存:每次请求时向服务器验证资源是否过期,通过
Last-Modified/If-Modified-Since
或ETag/If-None-Match
实现。
// 强缓存示例 - 设置Cache-Control
app.get('/static/js/main.js', (req, res) => {
res.setHeader('Cache-Control', 'public, max-age=31536000');
res.sendFile('/path/to/main.js');
});
// 协商缓存示例 - 使用ETag
app.get('/api/data', (req, res) => {
const data = getData();
const etag = generateETag(data);
if (req.headers['if-none-match'] === etag) {
return res.status(304).end();
}
res.setHeader('ETag', etag);
res.json(data);
});
缓存策略分类
静态资源缓存
静态资源如JS、CSS、图片等通常采用长期缓存策略:
- 文件名带哈希值:
main.a1b2c3d4.js
- 设置长期缓存头:
Cache-Control: public, max-age=31536000
<!-- 带哈希的文件名示例 -->
<script src="/static/js/main.a1b2c3d4.js"></script>
<link href="/static/css/styles.ef5g6h7i.css" rel="stylesheet">
动态API缓存
API响应缓存需要更精细的控制:
- 短时间缓存:
Cache-Control: max-age=60
- 私有缓存:
Cache-Control: private
- 禁止缓存:
Cache-Control: no-store
// API缓存策略示例
app.get('/api/user/profile', (req, res) => {
// 用户个人数据,缓存1分钟且仅限私有缓存
res.setHeader('Cache-Control', 'private, max-age=60');
res.json(getUserProfile(req.user.id));
});
Service Worker缓存
Service Worker提供更高级的缓存控制能力:
// Service Worker缓存策略示例
const CACHE_NAME = 'v1';
const ASSETS = [
'/styles/main.css',
'/scripts/app.js',
'/images/logo.png'
];
self.addEventListener('install', (event) => {
event.waitUntil(
caches.open(CACHE_NAME)
.then(cache => cache.addAll(ASSETS))
);
});
self.addEventListener('fetch', (event) => {
event.respondWith(
caches.match(event.request)
.then(response => response || fetch(event.request))
);
});
CDN缓存配置
CDN缓存需要与源站策略配合:
- 边缘节点缓存时间设置
- 缓存键规则(忽略查询参数、请求头等)
- 缓存刷新机制
# Nginx CDN缓存配置示例
location ~* \.(js|css|png|jpg|jpeg|gif|ico)$ {
expires 1y;
add_header Cache-Control "public, no-transform";
add_header X-Cache-Status $upstream_cache_status;
}
缓存更新策略
版本化文件名
通过文件内容哈希实现精确缓存更新:
// Webpack配置示例
output: {
filename: '[name].[contenthash:8].js',
chunkFilename: '[name].[contenthash:8].chunk.js'
}
缓存清除技术
- 查询参数版本号:
script.js?v=1.2.3
- 路径版本号:
/v1.2.3/script.js
- 服务端主动清除CDN缓存
缓存策略实践案例
案例一:电商网站商品图片
location ~* \.(jpg|jpeg|png|webp)$ {
# 长期缓存,但通过版本号更新
expires 1y;
add_header Cache-Control "public";
# 当图片不存在时尝试从CDN回源
try_files $uri @cdn_fallback;
}
案例二:实时数据API
app.get('/api/stock/prices', (req, res) => {
// 实时数据,禁用缓存
res.setHeader('Cache-Control', 'no-store, must-revalidate');
res.setHeader('Pragma', 'no-cache');
res.json(getRealTimeStockData());
});
缓存监控与调优
- 监控缓存命中率
- 分析用户访问模式
- 调整缓存时间策略
// 缓存命中率监控中间件
app.use((req, res, next) => {
const hit = res.getHeader('X-Cache-Hit');
statsd.increment(`cache.${hit ? 'hit' : 'miss'}`);
next();
});
常见问题解决方案
缓存污染问题
解决方案:
- 使用Vary头正确处理内容协商
- 避免过度使用查询参数作为缓存键
# 正确处理内容协商
location /api/data {
proxy_cache_key "$scheme$request_method$host$uri$http_accept";
proxy_cache_valid 200 10m;
add_header Vary "Accept";
}
移动端缓存限制
移动端特殊处理:
- 适当缩短缓存时间
- 考虑本地存储作为补充
// 移动端使用localStorage作为缓存后备
function fetchWithCache(url) {
const cached = localStorage.getItem(`cache:${url}`);
if (cached) {
return Promise.resolve(JSON.parse(cached));
}
return fetch(url)
.then(res => res.json())
.then(data => {
localStorage.setItem(`cache:${url}`, JSON.stringify(data));
return data;
});
}
高级缓存模式
缓存优先策略
// 缓存优先实现
async function cacheFirst(request) {
const cached = await caches.match(request);
if (cached) return cached;
const response = await fetch(request);
const cache = await caches.open('v1');
cache.put(request, response.clone());
return response;
}
网络优先策略
// 网络优先实现
async function networkFirst(request) {
try {
const response = await fetch(request);
const cache = await caches.open('v1');
cache.put(request, response.clone());
return response;
} catch (e) {
return await caches.match(request);
}
}
缓存安全考虑
- 敏感数据避免缓存
- 正确处理认证资源
- 防范缓存投毒攻击
# 敏感数据禁止缓存
location /api/account {
add_header Cache-Control "no-store, must-revalidate";
add_header Pragma "no-cache";
proxy_pass http://backend;
}
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn
上一篇:第三方脚本的优化加载
下一篇:关键渲染路径优化原则