本文目录导读:
我来详细解释源码资源缓存的实现逻辑,这是一个在前端工程化中非常重要的优化策略。
核心原理
源码资源缓存的核心思想是:利用浏览器的缓存机制,避免重复下载未变化的资源文件,主要通过哈希和HTTP缓存头来实现。
实现逻辑
文件指纹生成(构建阶段)
使用Webpack、Vite等构建工具时,会根据文件内容生成哈希值:
// webpack 配置示例
module.exports = {
output: {
filename: '[name].[contenthash:8].js',
chunkFilename: '[name].[contenthash:8].chunk.js',
}
}
// Vite 示例
export default defineConfig({
build: {
rollupOptions: {
output: {
entryFileNames: '[name].[hash].js',
chunkFileNames: '[name].[hash].js',
}
}
}
})
生成的文件名示例:
main.a3b8c2f1.jsvendor.8d7e6f5a.jsstyles.4c3b2a1d.css
缓存策略实现
// 服务端设置缓存头(Nginx 配置示例)
location /static/ {
# JS/CSS 等带hash的文件,设置长期缓存
if ($uri ~* \.[0-9a-f]{8,}\.(js|css)$) {
expires 1y;
add_header Cache-Control "public, immutable";
}
# HTML 文件不缓存或短期缓存
if ($uri ~* \.html$) {
expires -1;
add_header Cache-Control "no-cache, must-revalidate";
}
}
运行时缓存加载逻辑
/**
* 源码资源缓存管理器
*/
class SourceCacheManager {
constructor() {
this.cache = new Map();
this.loadingPromises = new Map();
this.cacheVersion = '1.0';
}
/**
* 加载JS资源
*/
async loadScript(url) {
// 1. 检查缓存
const cached = this.cache.get(url);
if (cached) {
console.log('缓存命中:', url);
return cached;
}
// 2. 防止并发加载
if (this.loadingPromises.has(url)) {
return this.loadingPromises.get(url);
}
// 3. 动态加载
const promise = new Promise((resolve, reject) => {
const script = document.createElement('script');
script.src = url;
script.onload = () => {
this.cache.set(url, { loaded: true });
this.loadingPromises.delete(url);
resolve();
};
script.onerror = (err) => {
this.loadingPromises.delete(url);
reject(err);
};
document.head.appendChild(script);
});
this.loadingPromises.set(url, promise);
return promise;
}
/**
* 预加载关键资源
*/
preloadCritical(url, as = 'script') {
const link = document.createElement('link');
link.rel = 'preload';
link.href = url;
link.as = as;
document.head.appendChild(link);
}
/**
* 清除缓存(版本更新时使用)
*/
clearCache() {
this.cache.clear();
this.cacheVersion = Date.now().toString();
}
}
Service Worker 缓存策略
// service-worker.js
const CACHE_NAME = 'source-cache-v1';
const DYNAMIC_CACHE = 'dynamic-cache-v1';
self.addEventListener('install', (event) => {
event.waitUntil(
caches.open(CACHE_NAME).then((cache) => {
return cache.addAll([
'/static/js/main.a3b8c2f1.js',
'/static/js/vendor.8d7e6f5a.js',
'/static/css/styles.4c3b2a1d.css'
]);
})
);
});
self.addEventListener('activate', (event) => {
event.waitUntil(
caches.keys().then((cacheNames) => {
return Promise.all(
cacheNames
.filter((name) => {
return name.startsWith('source-cache-') && name !== CACHE_NAME;
})
.map((name) => caches.delete(name))
);
})
);
});
self.addEventListener('fetch', (event) => {
// 缓存优先策略
if (event.request.url.includes('/static/')) {
event.respondWith(
caches.match(event.request).then((response) => {
// 返回缓存的资源
if (response) {
return response;
}
// 缓存未命中,发起网络请求
return fetch(event.request).then((networkResponse) => {
// 只缓存成功的响应
if (networkResponse.ok) {
const clonedResponse = networkResponse.clone();
caches.open(DYNAMIC_CACHE).then((cache) => {
cache.put(event.request, clonedResponse);
});
}
return networkResponse;
});
})
);
}
});
关键优化策略
版本控制与缓存失效
// 构建工具生成 manifest.json
{
"main.js": "main.a3b8c2f1.js",
"vendor.js": "vendor.8d7e6f5a.js",
"styles.css": "styles.4c3b2a1d.css"
}
// 运行时检查版本
async function checkAppVersion() {
try {
const manifest = await fetch('/manifest.json', { cache: 'no-cache' });
const manifestData = await manifest.json();
// 比较版本并清除过期缓存
if (manifestData.version !== localStorage.getItem('appVersion')) {
await caches.delete('dynamic-cache-v1');
localStorage.setItem('appVersion', manifestData.version);
}
} catch (error) {
console.error('Failed to check app version:', error);
}
}
分级缓存策略
class TieredCache {
constructor() {
this.memoryCache = new Map(); // 内存缓存
this.storageCache = new Map(); // localStorage缓存
this.serviceWorkerCache = null; // SW缓存
}
async get(url) {
// 1. 内存缓存 (最快)
const memoryResult = this.memoryCache.get(url);
if (memoryResult) return memoryResult;
// 2. localStorage缓存
const storageResult = localStorage.getItem(url);
if (storageResult) {
const parsed = JSON.parse(storageResult);
this.memoryCache.set(url, parsed.data);
return parsed.data;
}
// 3. 网络请求
const response = await fetch(url);
const data = await response.json();
// 存入各级缓存
this.memoryCache.set(url, data);
localStorage.setItem(url, JSON.stringify({ data, timestamp: Date.now() }));
return data;
}
// 内存缓存大小限制
evictMemoryCache() {
if (this.memoryCache.size > 50) {
const firstKey = this.memoryCache.keys().next().value;
this.memoryCache.delete(firstKey);
}
}
}
增量更新策略
class IncrementalUpdateManager {
constructor() {
this.updateQueue = new Set();
this.isUpdating = false;
}
async checkAndUpdate() {
// 获取当前版本资源列表
const currentAssets = await this.getCurrentAssets();
// 获取服务器最新资源列表
const serverAssets = await this.getServerAssets();
// 对比差异
const deletedAssets = currentAssets.filter(
url => !serverAssets.includes(url)
);
const newAssets = serverAssets.filter(
url => !currentAssets.includes(url)
);
// 清理已删除资源的缓存
deletedAssets.forEach(url => {
caches.delete(url);
localStorage.removeItem(url);
});
// 预加载新资源
newAssets.forEach(url => {
this.updateQueue.add(url);
});
// 批量更新
await this.processUpdateQueue();
}
async processUpdateQueue() {
if (this.isUpdating || this.updateQueue.size === 0) return;
this.isUpdating = true;
const batch = Array.from(this.updateQueue).slice(0, 5);
await Promise.all(
batch.map(url => this.loadAndCache(url))
);
batch.forEach(url => this.updateQueue.delete(url));
this.isUpdating = false;
// 继续处理剩余队列
if (this.updateQueue.size > 0) {
await this.processUpdateQueue();
}
}
}
实际应用示例
// 应用启动时的初始化
class AppInitializer {
async initialize() {
// 1. 检查Service Worker
if ('serviceWorker' in navigator) {
await navigator.serviceWorker.register('/service-worker.js');
}
// 2. 版本检查
const cacheManager = new SourceCacheManager();
await cacheManager.checkVersion();
// 3. 预加载关键资源
const criticalAssets = [
'/static/js/vendor.js',
'/static/js/main.js',
'/static/css/styles.css'
];
criticalAssets.forEach(url => cacheManager.preloadCritical(url));
// 4. 按需加载非关键资源
const lazyAssets = [
'/static/js/analytics.js',
'/static/js/third-party-lib.js'
];
requestIdleCallback(() => {
lazyAssets.forEach(url => cacheManager.loadScript(url));
});
}
}
性能监控
class CacheMonitor {
constructor() {
this.metrics = {
hits: 0,
misses: 0,
size: 0
};
}
recordHit() {
this.metrics.hits++;
}
recordMiss() {
this.metrics.misses++;
}
getHitRate() {
const total = this.metrics.hits + this.metrics.misses;
return total > 0 ? (this.metrics.hits / total * 100).toFixed(2) + '%' : '0%';
}
report() {
console.log('缓存统计:');
console.log(`命中率: ${this.getHitRate()}`);
console.log(`缓存大小: ${this.metrics.size}KB`);
}
}
- 哈希命名:确保文件变更时URL变化
- 分级缓存策略:内存 → localStorage → Service Worker
- 版本控制:通过manifest或版本号管理缓存失效
- 资源预加载:提前加载关键资源
- 懒加载优化:非关键资源按需加载
- 缓存监控:跟踪命中率和性能指标
- 错误处理:缓存失效时有优雅降级方案
这样实现的缓存系统可以有效减少网络请求,提升页面加载速度和用户体验。
标签: 源码资源缓存