前端缓存全解析:提升性能的关键策略
前端缓存技术深度解析:从原理到实践
目录
缓存概述
什么是缓存
缓存(Cache)是一种存储数据的技术,其核心目标是通过存储数据的副本,在后续请求中直接返回这些副本,从而提高数据访问速度和系统性能。
为什么需要缓存
在 Web 应用中,缓存的重要性不言而喻:
无缓存场景: 用户请求 → 服务器处理 → 数据库查询 → 数据处理 → 响应返回 ↑ ↓ 慢(多次网络往返 + 复杂计算) 耗时:500-2000ms 有缓存场景: 用户请求 → 缓存检查 → 直接返回 ↑ ↓ 快(内存访问) 耗时:1-50ms 缓存的优势
- 提升性能:减少网络请求,加快页面加载速度
- 降低服务器负载:减少数据库查询和计算
- 节省带宽:减少数据传输量
- 改善用户体验:离线可用,快速响应
- 降低成本:减少服务器资源消耗
缓存的挑战
- 数据一致性:缓存数据可能与源数据不同步
- 缓存穿透:请求不存在的数据导致缓存失效
- 缓存雪崩:大量缓存同时失效导致请求涌入后端
- 缓存击穿:热点数据失效时大量请求直达数据库
- 存储空间限制:浏览器存储空间有限
缓存层级模型
┌─────────────────────────────────────┐ │ 用户请求 │ └─────────────────┬───────────────────┘ │ ┌─────────────────▼───────────────────┐ │ 浏览器缓存 │ │ ┌──────────┐ ┌──────────┐ │ │ │ HTTP │ │ Service │ │ │ │ 缓存 │ │ Worker │ │ │ └──────────┘ └──────────┘ │ └─────────────────┬───────────────────┘ │ ┌─────────────────▼───────────────────┐ │ 应用内存缓存 │ │ ┌──────────────────────┐ │ │ │ React/Vue │ │ │ │ 状态缓存 │ │ │ └──────────────────────┘ │ └─────────────────┬───────────────────┘ │ ┌─────────────────▼───────────────────┐ │ 服务器缓存 │ │ ┌──────────────────────┐ │ │ │ Redis / Memcached │ │ │ └──────────────────────┘ │ └─────────────────┬───────────────────┘ │ ┌─────────────────▼───────────────────┐ │ 数据库缓存 │ │ ┌──────────────────────┐ │ │ │ 查询结果缓存 │ │ │ └──────────────────────┘ │ └─────────────────────────────────────┘ HTTP 缓存(浏览器缓存)
HTTP 缓存是浏览器最基础的缓存机制,通过 HTTP 头信息控制资源的缓存行为。
1. 强缓存
强缓存(Strong Cache)不会向服务器发送请求,直接从缓存中获取资源。
Cache-Control
Cache-Control: max-age=3600 常用指令:
max-age=<seconds>:缓存有效期(秒)no-cache:需要验证缓存有效性no-store:完全不使用缓存public:响应可被任何缓存存储private:响应只能被私有缓存存储immutable:资源不会更新
// Node.js 服务器端设置 app.get('/api/data',(req, res)=>{ res.set('Cache-Control','public, max-age=3600');// 缓存1小时 res.json({data:'some data'});});// 静态资源长期缓存 app.use('/static', express.static('public',{setHeaders:(res, path)=>{ res.set('Cache-Control','public, max-age=31536000, immutable');}}));# Nginx 配置 location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ { expires 1y; add_header Cache-Control "public, immutable"; } location /api/ { expires 5m; add_header Cache-Control "public"; } Expires
Expires: Wed, 21 Oct 2026 07:28:00 GMT // 设置过期时间 res.set('Expires',newDate(Date.now()+3600000).toUTCString());注意:Expires 是 HTTP/1.0 的产物,Cache-Control 是 HTTP/1.1 的改进,优先级更高。
2. 协商缓存
协商缓存(Conditional Cache)需要向服务器验证资源是否更新。
Last-Modified / If-Modified-Since
Last-Modified: Wed, 21 Oct 2026 07:28:00 GMT // 服务器端实现 app.get('/api/data',(req, res)=>{const lastModified =newDate('2026-01-05').toUTCString();const ifModifiedSince = req.get('If-Modified-Since');if(ifModifiedSince === lastModified){return res.status(304).end();// 未修改} res.set('Last-Modified', lastModified); res.json({data:'some data'});});ETag / If-None-Match
ETag: "abc123" // 服务器端实现const crypto =require('crypto'); app.get('/api/data',(req, res)=>{const data =JSON.stringify({data:'some data'});const etag = crypto.createHash('md5').update(data).digest('hex');const ifNoneMatch = req.get('If-None-Match');if(ifNoneMatch === etag){return res.status(304).end();} res.set('ETag', etag); res.json(JSON.parse(data));});ETag vs Last-Modified:
| 特性 | ETag | Last-Modified |
|---|---|---|
| 精度 | 基于内容哈希 | 基于时间戳 |
| 准确性 | 更高 | 可能不准确(秒级) |
| 优先级 | 更高 | 较低 |
| 支持度 | 现代浏览器广泛支持 | 所有浏览器支持 |
3. 缓存控制指令
常用指令组合
// 不缓存 res.set('Cache-Control','no-store, no-cache, must-revalidate');// 短时间缓存 res.set('Cache-Control','public, max-age=300');// 5分钟// 长时间缓存(静态资源) res.set('Cache-Control','public, max-age=31536000, immutable');// 私有缓存(敏感数据) res.set('Cache-Control','private, max-age=3600');// 代理缓存 res.set('Cache-Control','public, s-maxage=3600');Vary 头
Vary: Accept-Encoding // 根据 Accept-Encoding 缓存不同版本 res.set('Vary','Accept-Encoding, User-Agent');Service Worker 缓存
Service Worker 是在浏览器后台运行的脚本,提供了强大的缓存控制能力。
1. Cache Storage
// 注册 Service Workerif('serviceWorker'in navigator){ navigator.serviceWorker.register('/sw.js').then(registration=>{ console.log('SW registered:', registration);});}// sw.js - Service Worker 文件constCACHE_NAME='my-app-v1';const urlsToCache =['/','/static/js/main.js','/static/css/main.css','/api/user-data'];// 安装阶段 - 缓存资源 self.addEventListener('install',event=>{ event.waitUntil( caches.open(CACHE_NAME).then(cache=>{ console.log('Opened cache');return cache.addAll(urlsToCache);}));});// 激活阶段 - 清理旧缓存 self.addEventListener('activate',event=>{ event.waitUntil( caches.keys().then(cacheNames=>{return Promise.all( cacheNames.map(cacheName=>{if(cacheName !==CACHE_NAME){return caches.delete(cacheName);}}));}));});// 拦截请求 self.addEventListener('fetch',event=>{ event.respondWith( caches.match(event.request).then(response=>{// 如果缓存中有,直接返回if(response){return response;}// 否则请求网络returnfetch(event.request);}));});2. 缓存策略
Cache First(缓存优先)
self.addEventListener('fetch',event=>{// 只缓存 GET 请求if(event.request.method !=='GET')return; event.respondWith( caches.match(event.request).then(cachedResponse=>{if(cachedResponse){return cachedResponse;}returnfetch(event.request).then(response=>{// 检查响应是否有效if(!response || response.status !==200|| response.type !=='basic'){return response;}// 克隆响应并缓存const responseToCache = response.clone(); caches.open(CACHE_NAME).then(cache=>{ cache.put(event.request, responseToCache);});return response;});}));});Network First(网络优先)
self.addEventListener('fetch',event=>{if(event.request.method !=='GET')return; event.respondWith(fetch(event.request).then(response=>{// 网络请求成功,缓存响应const responseClone = response.clone(); caches.open(CACHE_NAME).then(cache=>{ cache.put(event.request, responseClone);});return response;}).catch(()=>{// 网络请求失败,使用缓存return caches.match(event.request);}));});Stale While Revalidate(过期数据可用,同时后台更新)
self.addEventListener('fetch',event=>{if(event.request.method !=='GET')return; event.respondWith( caches.match(event.request).then(cachedResponse=>{const networkFetch =fetch(event.request).then(response=>{// 更新缓存const responseClone = response.clone(); caches.open(CACHE_NAME).then(cache=>{ cache.put(event.request, responseClone);});return response;}).catch(()=>{// 网络请求失败 console.error('Network fetch failed');});// 立即返回缓存,同时后台更新return cachedResponse || networkFetch;}));});3. 离线优先
// IndexedDB 辅助类classIDBHelper{constructor(dbName, storeName){this.dbName = dbName;this.storeName = storeName;this.db =null;}asyncinit(){returnnewPromise((resolve, reject)=>{const request = indexedDB.open(this.dbName,1); request.onerror=()=>reject(request.error); request.onsuccess=()=>{this.db = request.result;resolve(this.db);}; request.onupgradeneeded=(event)=>{const db = event.target.result;if(!db.objectStoreNames.contains(this.storeName)){ db.createObjectStore(this.storeName,{keyPath:'id'});}};});}asyncget(key){returnnewPromise((resolve, reject)=>{const transaction =this.db.transaction([this.storeName],'readonly');const store = transaction.objectStore(this.storeName);const request = store.get(key); request.onerror=()=>reject(request.error); request.onsuccess=()=>resolve(request.result);});}asyncset(value){returnnewPromise((resolve, reject)=>{const transaction =this.db.transaction([this.storeName],'readwrite');const store = transaction.objectStore(this.storeName);const request = store.put(value); request.onerror=()=>reject(request.error); request.onsuccess=()=>resolve(request.result);});}}// 使用 IndexedDB 缓存数据const idbHelper =newIDBHelper('MyAppDB','apiCache'); self.addEventListener('fetch',event=>{if(event.request.url.includes('/api/')){ event.respondWith((async()=>{try{// 尝试网络请求const networkResponse =awaitfetch(event.request);// 缓存响应if(networkResponse.ok){await idbHelper.set({id: event.request.url,data:await networkResponse.clone().json(),timestamp: Date.now()});}return networkResponse;}catch(error){// 网络失败,尝试缓存const cached =await idbHelper.get(event.request.url);if(cached){returnnewResponse(JSON.stringify(cached.data),{headers:{'Content-Type':'application/json'}});}// 没有缓存,返回离线页面returnnewResponse('Offline',{status:503});}})());}});内存缓存
内存缓存是在应用运行期间将数据存储在内存中的缓存方式。
1. 内存数据结构
Map 缓存
classSimpleCache{constructor(){this.cache =newMap();}set(key, value, ttl =0){const expires = ttl >0? Date.now()+ ttl :0;this.cache.set(key,{ value, expires });}get(key){const item =this.cache.get(key);if(!item)returnnull;// 检查是否过期if(item.expires && item.expires < Date.now()){this.cache.delete(key);returnnull;}return item.value;}delete(key){this.cache.delete(key);}clear(){this.cache.clear();}// 获取缓存大小size(){returnthis.cache.size;}// 清理过期项cleanup(){const now = Date.now();for(const[key, item]ofthis.cache.entries()){if(item.expires && item.expires < now){this.cache.delete(key);}}}}// 使用示例const cache =newSimpleCache();// 缓存用户信息,10分钟过期 cache.set('user:123',{name:'张三',age:25},10*60*1000);const user = cache.get('user:123'); console.log(user);// { name: '张三', age: 25 }WeakMap 缓存
classWeakRefCache{constructor(){this.cache =newWeakMap();this.timeouts =newWeakMap();}set(key, value, ttl =0){this.cache.set(key,{ value,timestamp: Date.now()});if(ttl >0){const timeoutId =setTimeout(()=>{this.delete(key);}, ttl);this.timeouts.set(key, timeoutId);}}get(key){const item =this.cache.get(key);return item ? item.value :null;}delete(key){this.cache.delete(key);const timeoutId =this.timeouts.get(key);if(timeoutId){clearTimeout(timeoutId);this.timeouts.delete(key);}}}// 使用示例const weakCache =newWeakRefCache();// 缓存 DOM 元素相关数据const element = document.querySelector('#myElement'); weakCache.set(element,{data:'some data'},5*60*1000);2. LRU 缓存实现
classLRUCache{constructor(limit =100){this.limit = limit;this.cache =newMap();}get(key){if(!this.cache.has(key))returnnull;const value =this.cache.get(key).value;// 将访问的项移到末尾(最近使用)this.cache.delete(key);this.cache.set(key,{ value,timestamp: Date.now()});return value;}set(key, value, ttl =0){// 检查是否已存在if(this.cache.has(key)){this.cache.delete(key);}elseif(this.cache.size >=this.limit){// 删除最久未使用的项(第一个)const firstKey =this.cache.keys().next().value;this.cache.delete(firstKey);}const expires = ttl >0? Date.now()+ ttl :0;this.cache.set(key,{ value, expires,timestamp: Date.now()});}has(key){const item =this.cache.get(key);if(!item)returnfalse;if(item.expires && item.expires < Date.now()){this.cache.delete(key);returnfalse;}returntrue;}delete(key){this.cache.delete(key);}clear(){this.cache.clear();}// 获取所有有效项getValidItems(){const now = Date.now();const validItems =[];for(const[key, item]ofthis.cache.entries()){if(!item.expires || item.expires >= now){ validItems.push({ key,value: item.value });}else{this.cache.delete(key);}}return validItems;}}// 使用示例const lruCache =newLRUCache(50);// 缓存 API 响应asyncfunctionfetchWithCache(url, ttl =5*60*1000){if(lruCache.has(url)){ console.log('使用缓存:', url);return lruCache.get(url);}const response =awaitfetch(url);const data =await response.json(); lruCache.set(url, data, ttl);return data;}3. 内存缓存管理
缓存管理器
classCacheManager{constructor(){this.caches =newMap();this.cleanupInterval =null;}createCache(name, options ={}){const{ limit =100, ttl =0}= options;if(this.caches.has(name)){returnthis.caches.get(name);}const cache =newLRUCache(limit);const cacheWrapper ={ name,set:(key, value)=> cache.set(key, value, ttl),get:(key)=> cache.get(key),has:(key)=> cache.has(key),delete:(key)=> cache.delete(key),clear:()=> cache.clear(),size:()=> cache.cache.size,getStats:()=>({ name,size: cache.cache.size, limit, ttl })};this.caches.set(name, cacheWrapper);return cacheWrapper;}getCache(name){returnthis.caches.get(name);}deleteCache(name){const cache =this.caches.get(name);if(cache){ cache.clear();this.caches.delete(name);}}clearAll(){for(const cache ofthis.caches.values()){ cache.clear();}}getAllStats(){const stats =[];for(const cache ofthis.caches.values()){ stats.push(cache.getStats());}return stats;}// 启动定期清理startCleanup(interval =60000){if(this.cleanupInterval)return;this.cleanupInterval =setInterval(()=>{for(const cache ofthis.caches.values()){const validItems = cache.getValidItems?.();if(validItems){ console.log(`清理缓存 ${cache.name},剩余有效项: ${validItems.length}`);}}}, interval);}stopCleanup(){if(this.cleanupInterval){clearInterval(this.cleanupInterval);this.cleanupInterval =null;}}}// 全局缓存管理器实例const cacheManager =newCacheManager();// 创建不同类型的缓存const apiCache = cacheManager.createCache('api',{limit:200,ttl:300000});const componentCache = cacheManager.createCache('component',{limit:50,ttl:600000});const userCache = cacheManager.createCache('user',{limit:100,ttl:600000});// 启动定期清理 cacheManager.startCleanup();// 在页面卸载时清理 window.addEventListener('beforeunload',()=>{ cacheManager.stopCleanup();});状态管理缓存
1. Redux 缓存
// 使用 redux-persist 持久化状态import{ persistStore, persistReducer }from'redux-persist';import storage from'redux-persist/lib/storage';import{ configureStore }from'@reduxjs/toolkit';import userReducer from'./userSlice';// 配置持久化const persistConfig ={key:'root', storage,whitelist:['user','settings'],// 只持久化这些 reducerblacklist:['temp']// 不持久化这些 reducer};const persistedReducer =persistReducer(persistConfig, rootReducer);const store =configureStore({reducer: persistedReducer,middleware:(getDefaultMiddleware)=>getDefaultMiddleware({serializableCheck:{ignoredActions:[FLUSH,REHYDRATE,PAUSE,PERSIST,PURGE,REGISTER]}})});const persistor =persistStore(store);// 在组件中使用import{ useSelector }from'react-redux';functionUserProfile(){const user =useSelector((state)=> state.user.data);const isLoading =useSelector((state)=> state.user.loading);if(isLoading)return<div>加载中...</div>;return<div>{user.name}</div>;}自定义中间件缓存
// API 缓存中间件constapiCacheMiddleware=(store)=>(next)=>(action)=>{const{ type, payload }= action;// 如果是 API 请求if(type.startsWith('api/')){const cacheKey =JSON.stringify(payload);const cachedData =getFromCache(cacheKey);if(cachedData &&!payload.forceRefresh){// 返回缓存数据return{type:`${type}_SUCCESS`,payload: cachedData };}}const result =next(action);// 如果请求成功,缓存结果if(type.endsWith('_SUCCESS')){const{ url, ttl =300000}= payload;setCache(url, action.payload, ttl);}return result;};// 使用缓存中间件const store =configureStore({reducer: rootReducer,middleware:(getDefaultMiddleware)=>getDefaultMiddleware().concat(apiCacheMiddleware)});2. Vuex/Pinia 缓存
// Pinia 缓存插件exportfunctioncachePlugin({ store }){// 从 localStorage 恢复状态const savedState = localStorage.getItem('pinia-state');if(savedState){ store.$patch(JSON.parse(savedState));}// 状态变化时保存到 localStorage store.$subscribe((mutation, state)=>{ localStorage.setItem('pinia-state',JSON.stringify(state));});}// main.jsimport{ createPinia }from'pinia';import{ cachePlugin }from'./plugins/cache';const pinia =createPinia(); pinia.use(cachePlugin); app.use(pinia);Pinia 缓存 Store
// stores/cache.jsimport{ defineStore }from'pinia';import axios from'axios';exportconst useCacheStore =defineStore('cache',{state:()=>({_cache:newMap(),_timestamps:newMap()}),actions:{asyncget(key, fetcher, ttl =300000){// 检查缓存是否存在且未过期if(this._cache.has(key)){const timestamp =this._timestamps.get(key);if(Date.now()- timestamp < ttl){returnthis._cache.get(key);}}// 获取新数据const data =awaitfetcher();this._cache.set(key, data);this._timestamps.set(key, Date.now());return data;},set(key, value){this._cache.set(key, value);this._timestamps.set(key, Date.now());},delete(key){this._cache.delete(key);this._timestamps.delete(key);},clear(){this._cache.clear();this._timestamps.clear();}}});// 使用缓存 Storeconst cacheStore =useCacheStore();const userData =await cacheStore.get('user:123',async()=>{const response =await axios.get('/api/user/123');return response.data;});3. React Query 缓存
// React Query 配置import{ QueryClient, QueryClientProvider }from'@tanstack/react-query';const queryClient =newQueryClient({defaultOptions:{queries:{staleTime:5*60*1000,// 5分钟内数据新鲜cacheTime:10*60*1000,// 10分钟后清理缓存retry:3,refetchOnWindowFocus:false}}});functionApp(){return(<QueryClientProvider client={queryClient}><UserProfile /></QueryClientProvider>);}// 使用 React Queryimport{ useQuery }from'@tanstack/react-query';functionUserProfile({ userId }){const{ data, isLoading, error }=useQuery(['user', userId],async()=>{const response =awaitfetch(`/api/user/${userId}`);if(!response.ok)thrownewError('Network error');return response.json();},{staleTime:5*60*1000,// 缓存5分钟cacheTime:10*60*1000,enabled:!!userId // 只有 userId 存在时才执行查询});if(isLoading)return<div>加载中...</div>;if(error)return<div>错误:{error.message}</div>;return<div>{data.name}</div>;}// 手动操作缓存functionupdateUser(){ queryClient.setQueryData(['user', userId],(oldData)=>({...oldData,name:'新名称'}));// 失效缓存 queryClient.invalidateQueries(['user']);}浏览器存储缓存
1. LocalStorage
// LocalStorage 封装类classLocalStorageCache{constructor(prefix ='cache_'){this.prefix = prefix;}set(key, value, ttl =0){const data ={ value,timestamp: Date.now(),ttl: ttl >0? ttl :null}; localStorage.setItem(this.prefix + key,JSON.stringify(data));}get(key){const item = localStorage.getItem(this.prefix + key);if(!item)returnnull;try{const data =JSON.parse(item);// 检查是否过期if(data.ttl && Date.now()- data.timestamp > data.ttl){this.delete(key);returnnull;}return data.value;}catch(error){ console.error('Parse error:', error);this.delete(key);returnnull;}}delete(key){ localStorage.removeItem(this.prefix + key);}clear(){const keys = Object.keys(localStorage); keys.forEach(key=>{if(key.startsWith(this.prefix)){ localStorage.removeItem(key);}});}// 获取所有缓存键getAllKeys(){return Object.keys(localStorage).filter(key=> key.startsWith(this.prefix)).map(key=> key.replace(this.prefix,''));}}// 使用示例const localCache =newLocalStorageCache();// 缓存用户设置 localCache.set('userSettings',{theme:'dark',language:'zh-CN'},24*60*60*1000);// 24小时const settings = localCache.get('userSettings'); console.log(settings);// { theme: 'dark', language: 'zh-CN' }2. SessionStorage
// SessionStorage 缓存classSessionStorageCache{constructor(prefix ='session_'){this.prefix = prefix;}set(key, value){const data ={ value,timestamp: Date.now()}; sessionStorage.setItem(this.prefix + key,JSON.stringify(data));}get(key){const item = sessionStorage.getItem(this.prefix + key);if(!item)returnnull;try{const data =JSON.parse(item);return data.value;}catch(error){ console.error('Parse error:', error);this.delete(key);returnnull;}}delete(key){ sessionStorage.removeItem(this.prefix + key);}clear(){const keys = Object.keys(sessionStorage); keys.forEach(key=>{if(key.startsWith(this.prefix)){ sessionStorage.removeItem(key);}});}}// 使用示例const sessionCache =newSessionStorageCache();// 缓存表单数据 sessionCache.set('formData',{name:'张三',email:'[email protected]'});// 页面刷新后数据仍然存在(会话期间)const formData = sessionCache.get('formData');3. IndexedDB
// IndexedDB 封装classIndexedDBCache{constructor(dbName, storeName){this.dbName = dbName;this.storeName = storeName;this.db =null;}asyncinit(){returnnewPromise((resolve, reject)=>{const request = indexedDB.open(this.dbName,1); request.onerror=()=>reject(request.error); request.onsuccess=()=>{this.db = request.result;resolve(this.db);}; request.onupgradeneeded=(event)=>{const db = event.target.result;if(!db.objectStoreNames.contains(this.storeName)){const store = db.createObjectStore(this.storeName,{keyPath:'key'}); store.createIndex('timestamp','timestamp',{unique:false});}};});}asyncset(key, value, ttl =0){const data ={ key, value,timestamp: Date.now(), ttl };returnnewPromise((resolve, reject)=>{const transaction =this.db.transaction([this.storeName],'readwrite');const store = transaction.objectStore(this.storeName);const request = store.put(data); request.onerror=()=>reject(request.error); request.onsuccess=()=>resolve(request.result);});}asyncget(key){returnnewPromise((resolve, reject)=>{const transaction =this.db.transaction([this.storeName],'readonly');const store = transaction.objectStore(this.storeName);const request = store.get(key); request.onerror=()=>reject(request.error); request.onsuccess=()=>{const data = request.result;if(!data){resolve(null);return;}// 检查是否过期if(data.ttl && Date.now()- data.timestamp > data.ttl){this.delete(key);resolve(null);return;}resolve(data.value);};});}asyncdelete(key){returnnewPromise((resolve, reject)=>{const transaction =this.db.transaction([this.storeName],'readwrite');const store = transaction.objectStore(this.storeName);const request = store.delete(key); request.onerror=()=>reject(request.error); request.onsuccess=()=>resolve(request.result);});}asyncclear(){returnnewPromise((resolve, reject)=>{const transaction =this.db.transaction([this.storeName],'readwrite');const store = transaction.objectStore(this.storeName);const request = store.clear(); request.onerror=()=>reject(request.error); request.onsuccess=()=>resolve(request.result);});}asyncgetAll(){returnnewPromise((resolve, reject)=>{const transaction =this.db.transaction([this.storeName],'readonly');const store = transaction.objectStore(this.storeName);const request = store.getAll(); request.onerror=()=>reject(request.error); request.onsuccess=()=>resolve(request.result);});}}// 使用示例const idbCache =newIndexedDBCache('MyApp','apiCache');await idbCache.init();// 缓存大量数据await idbCache.set('user:123',{profile:{name:'张三',age:25},posts:[...],// 大数组preferences:{...}},3600000);// 1小时const userData =await idbCache.get('user:123');缓存策略详解
1. Cache First
// Cache First 策略:先查缓存,缓存没有再请求网络asyncfunctioncacheFirst(request, cacheName){const cache =await caches.open(cacheName);const cachedResponse =await cache.match(request);if(cachedResponse){return cachedResponse;}const networkResponse =awaitfetch(request);if(networkResponse.ok){ cache.put(request, networkResponse.clone());}return networkResponse;}// 适用场景:静态资源(CSS、JS、图片)const cssResponse =awaitcacheFirst('/static/css/main.css','assets-cache');2. Network First
// Network First 策略:先请求网络,失败则使用缓存asyncfunctionnetworkFirst(request, cacheName, timeout =5000){const cache =await caches.open(cacheName);try{// 设置超时const networkPromise =fetch(request);const timeoutPromise =newPromise((resolve, reject)=>{setTimeout(()=>reject(newError('Timeout')), timeout);});const response =await Promise.race([networkPromise, timeoutPromise]);if(response.ok){ cache.put(request, response.clone());}return response;}catch(error){// 网络失败,使用缓存const cachedResponse =await cache.match(request);if(cachedResponse){return cachedResponse;}throw error;}}// 适用场景:API 数据const userData =awaitnetworkFirst('/api/user/123','api-cache');3. Stale While Revalidate
// Stale While Revalidate 策略:返回缓存,同时后台更新asyncfunctionstaleWhileRevalidate(request, cacheName){const cache =await caches.open(cacheName);const cachedResponse =await cache.match(request);const fetchPromise =fetch(request).then(response=>{if(response.ok){ cache.put(request, response.clone());}return response;}).catch(error=>{ console.error('Fetch failed:', error);returnnull;});// 立即返回缓存,同时后台更新return cachedResponse || fetchPromise;}// 适用场景:新闻文章、博客内容const articles =awaitstaleWhileRevalidate('/api/articles','articles-cache');4. Cache Only
// Cache Only 策略:只从缓存获取asyncfunctioncacheOnly(request, cacheName){const cache =await caches.open(cacheName);const cachedResponse =await cache.match(request);if(!cachedResponse){thrownewError('No cached response found');}return cachedResponse;}// 适用场景:离线页面const offlinePage =awaitcacheOnly('/offline.html','offline-cache');5. Network Only
// Network Only 策略:只从网络获取asyncfunctionnetworkOnly(request){returnawaitfetch(request);}// 适用场景:登录、支付等敏感操作const loginResponse =awaitnetworkOnly('/api/login',{method:'POST',body:JSON.stringify(credentials)});策略选择决策树
需要缓存的资源 | ├─ 静态资源(CSS、JS、图片) → Cache First │ └─ 长期缓存(1年)+ 版本控制 │ ├─ API 数据(用户信息、商品列表) → Network First │ └─ 短期缓存(5分钟)+ 错误回退 │ ├─ 频繁更新的内容(新闻、博客) → Stale While Revalidate │ └─ 显示缓存 + 后台更新 │ ├─ 离线页面 → Cache Only │ └─ 预缓存关键页面 │ └─ 敏感操作(登录、支付) → Network Only └─ 不缓存,确保数据最新 实际应用场景
1. API 数据缓存
用户信息缓存
classUserCache{constructor(){this.cache =newLRUCache(50);this.apiCache =newLocalStorageCache('user_');}asyncgetUser(userId, forceRefresh =false){const cacheKey =`user:${userId}`;// 强制刷新或内存缓存中没有if(!forceRefresh){const cached =this.cache.get(cacheKey);if(cached){ console.log('从内存缓存获取用户:', userId);return cached;}}// 检查本地存储缓存const localCached =this.apiCache.get(cacheKey);if(localCached &&!forceRefresh){ console.log('从本地存储获取用户:', userId);this.cache.set(cacheKey, localCached);return localCached;}// 请求 APItry{const response =awaitfetch(`/api/user/${userId}`);if(!response.ok)thrownewError('Failed to fetch');const userData =await response.json();// 更新缓存this.cache.set(cacheKey, userData);this.apiCache.set(cacheKey, userData,10*60*1000);// 10分钟return userData;}catch(error){ console.error('Failed to fetch user:', error);// 返回本地缓存(如果有)if(localCached){return localCached;}throw error;}}updateUser(userId, userData){const cacheKey =`user:${userId}`;this.cache.set(cacheKey, userData);this.apiCache.set(cacheKey, userData,10*60*1000);}clearUser(userId){const cacheKey =`user:${userId}`;this.cache.delete(cacheKey);this.apiCache.delete(cacheKey);}}const userCache =newUserCache();// 使用const user =await userCache.getUser('123'); userCache.updateUser('123',{...user,name:'新名称'});分页数据缓存
classPaginatedCache{constructor(){this.cache =newMap();// key: `${url}:${page}`, value: { data, timestamp }this.cacheTime =5*60*1000;// 5分钟}asyncgetPage(url, page, params ={}){const cacheKey =`${url}:${page}:${JSON.stringify(params)}`;const cached =this.cache.get(cacheKey);if(cached && Date.now()- cached.timestamp <this.cacheTime){return cached.data;}const queryParams =newURLSearchParams({ page,...params });const response =awaitfetch(`${url}?${queryParams}`);if(!response.ok)thrownewError('Failed to fetch');const data =await response.json();this.cache.set(cacheKey,{ data,timestamp: Date.now()});return data;}invalidatePage(url, page){// 删除指定页的缓存for(const key ofthis.cache.keys()){if(key.startsWith(`${url}:${page}:`)){this.cache.delete(key);}}}invalidateUrl(url){// 删除指定 URL 的所有缓存for(const key ofthis.cache.keys()){if(key.startsWith(`${url}:`)){this.cache.delete(key);}}}}const paginatedCache =newPaginatedCache();// 使用const products =await paginatedCache.getPage('/api/products',1,{category:'electronics',sort:'price'});2. 图片资源缓存
图片预加载
classImageCache{constructor(maxCache =50){this.cache =newMap();// key: url, value: { img, timestamp }this.maxCache = maxCache;}preloadImage(url){returnnewPromise((resolve, reject)=>{// 检查缓存const cached =this.cache.get(url);if(cached){resolve(cached.img);return;}const img =newImage(); img.onload=()=>{// 添加到缓存this.cache.set(url,{ img,timestamp: Date.now()});// 如果缓存超过限制,删除最旧的if(this.cache.size >this.maxCache){const firstKey =this.cache.keys().next().value;this.cache.delete(firstKey);}resolve(img);}; img.onerror = reject; img.src = url;});}getImage(url){const cached =this.cache.get(url);return cached ? cached.img :null;}clear(){this.cache.clear();}}const imageCache =newImageCache();// 预加载图片functionpreloadImages(urls){return Promise.all(urls.map(url=> imageCache.preloadImage(url)));}// 使用preloadImages(['/images/hero.jpg','/images/product1.jpg','/images/product2.jpg']).then(()=>{ console.log('所有图片预加载完成');});图片懒加载
classLazyImageLoader{constructor(){this.imageCache =newImageCache(100);this.observer =null;this.init();}init(){// Intersection Observer 实现懒加载if('IntersectionObserver'in window){this.observer =newIntersectionObserver((entries)=>{ entries.forEach(entry=>{if(entry.isIntersecting){const img = entry.target;const src = img.dataset.src;this.loadImage(img, src);this.observer.unobserve(img);}});},{rootMargin:'50px'// 提前50px开始加载});}}asyncloadImage(img, src){try{const image =awaitthis.imageCache.preloadImage(src); img.src = image.src; img.classList.remove('lazy');}catch(error){ console.error('Failed to load image:', error); img.classList.add('error');}}observe(img){if(this.observer){this.observer.observe(img);}else{// 降级方案:直接加载const src = img.dataset.src;this.loadImage(img, src);}}}const lazyImageLoader =newLazyImageLoader();// HTML// <img alt="示例">// <img alt="示例">// JavaScript document.querySelectorAll('img.lazy').forEach(img=>{ lazyImageLoader.observe(img);});3. 页面组件缓存
React 组件缓存
// 使用 React.memo 和 useMemo 缓存组件const ExpensiveComponent = React.memo(({ data, filter })=>{const filteredData =useMemo(()=>{return data.filter(item=> item.category === filter);},[data, filter]);return(<div>{filteredData.map(item=>(<div key={item.id}>{item.name}</div>))}</div>);});// 使用 useCallback 缓存函数functionParentComponent(){const[count, setCount]=useState(0);const handleClick =useCallback(()=>{setCount(prev=> prev +1);},[]);return(<div><button onClick={handleClick}>点击 {count} 次</button><ExpensiveComponent data={expensiveData} filter="active"/></div>);}动态组件缓存
classComponentCache{constructor(){this.cache =newMap();}asyncloadComponent(importFunc){const modulePath = importFunc.toString();// 检查缓存if(this.cache.has(modulePath)){returnthis.cache.get(modulePath);}// 动态加载组件const module =awaitimportFunc();const Component = module.default;// 缓存组件this.cache.set(modulePath, Component);return Component;}clear(){this.cache.clear();}}const componentCache =newComponentCache();// 动态加载路由组件const routes =[{path:'/user',component:()=>import('./UserComponent')},{path:'/product',component:()=>import('./ProductComponent')}];// 渲染路由functionrenderRoute(route){ componentCache.loadComponent(route.component).then(Component=>{ ReactDOM.render(<Component />, document.getElementById('root'));});}性能优化实践
1. 缓存分层策略
// 多层缓存架构classMultiLayerCache{constructor(){this.l1Cache =newMap();// 内存缓存,最快this.l2Cache =newLocalStorageCache('l2_');// 本地存储this.l3Cache =newIndexedDBCache('MyApp','l3');// IndexedDB}asyncget(key){// L1 缓存if(this.l1Cache.has(key)){returnthis.l1Cache.get(key);}// L2 缓存const l2Data =this.l2Cache.get(key);if(l2Data){this.l1Cache.set(key, l2Data);return l2Data;}// L3 缓存const l3Data =awaitthis.l3Cache.get(key);if(l3Data){this.l1Cache.set(key, l3Data);this.l2Cache.set(key, l3Data);return l3Data;}returnnull;}asyncset(key, value, ttl =0){// 设置所有层级this.l1Cache.set(key, value);this.l2Cache.set(key, value, ttl);awaitthis.l3Cache.set(key, value, ttl);}asyncdelete(key){this.l1Cache.delete(key);this.l2Cache.delete(key);awaitthis.l3Cache.delete(key);}clear(){this.l1Cache.clear();this.l2Cache.clear();this.l3Cache.clear();}}const multiLayerCache =newMultiLayerCache();2. 缓存预热
// 缓存预热classCacheWarmer{constructor(cache){this.cache = cache;}asyncwarmUp(config){ console.log('开始缓存预热...');const promises = config.map(async({ key, fetcher, ttl })=>{try{const data =awaitfetcher();awaitthis.cache.set(key, data, ttl); console.log(`预热成功: ${key}`);}catch(error){ console.error(`预热失败: ${key}`, error);}});await Promise.all(promises); console.log('缓存预热完成');}}// 配置预热数据const warmer =newCacheWarmer(multiLayerCache); warmer.warmUp([{key:'user:123',fetcher:()=>fetch('/api/user/123').then(r=> r.json()),ttl:10*60*1000},{key:'menu-items',fetcher:()=>fetch('/api/menu').then(r=> r.json()),ttl:30*60*1000},{key:'config',fetcher:()=>fetch('/api/config').then(r=> r.json()),ttl:60*60*1000}]);3. 缓存压缩
// 缓存数据压缩import LZString from'lz-string';classCompressedCache{constructor(cache){this.cache = cache;}asyncset(key, value, ttl =0){// 压缩数据const compressed = LZString.compress(JSON.stringify(value));awaitthis.cache.set(key, compressed, ttl);}asyncget(key){const compressed =awaitthis.cache.get(key);if(!compressed)returnnull;try{const decompressed = LZString.decompress(compressed);returnJSON.parse(decompressed);}catch(error){ console.error('Failed to decompress:', error);returnnull;}}}const compressedCache =newCompressedCache(multiLayerCache);4. 缓存统计
// 缓存统计classCacheStats{constructor(){this.hits =0;this.misses =0;this.size =0;}recordHit(){this.hits++;}recordMiss(){this.misses++;}gethitRate(){const total =this.hits +this.misses;return total >0?(this.hits / total)*100:0;}getStats(){const total =this.hits +this.misses;return{hits:this.hits,misses:this.misses, total,hitRate:this.hitRate.toFixed(2)+'%',size:this.size };}reset(){this.hits =0;this.misses =0;}}// 带统计的缓存classStatsCache{constructor(cache){this.cache = cache;this.stats =newCacheStats();}asyncget(key){const value =awaitthis.cache.get(key);if(value){this.stats.recordHit();}else{this.stats.recordMiss();}return value;}asyncset(key, value, ttl){returnthis.cache.set(key, value, ttl);}getStats(){returnthis.stats.getStats();}resetStats(){this.stats.reset();}}const statsCache =newStatsCache(multiLayerCache);// 定期输出统计信息setInterval(()=>{ console.log('缓存统计:', statsCache.getStats());},60000);缓存失效与更新
1. TTL(Time To Live)
// TTL 管理classTTLCache{constructor(){this.cache =newMap();this.timeouts =newMap();}set(key, value, ttl){// 设置缓存this.cache.set(key, value);// 清除之前的定时器if(this.timeouts.has(key)){clearTimeout(this.timeouts.get(key));}// 设置新的定时器if(ttl >0){const timeoutId =setTimeout(()=>{this.delete(key);}, ttl);this.timeouts.set(key, timeoutId);}}get(key){returnthis.cache.get(key);}delete(key){this.cache.delete(key);const timeoutId =this.timeouts.get(key);if(timeoutId){clearTimeout(timeoutId);this.timeouts.delete(key);}}clear(){// 清除所有定时器for(const timeoutId ofthis.timeouts.values()){clearTimeout(timeoutId);}this.timeouts.clear();this.cache.clear();}}2. 版本控制
// 基于版本的缓存失效classVersionedCache{constructor(){this.cache =newMap();this.version ='v1';}setVersion(newVersion){if(this.version !== newVersion){this.version = newVersion;this.clear();// 版本变化时清除所有缓存}}set(key, value, ttl =0){const versionedKey =`${this.version}:${key}`;this.cache.set(versionedKey,{ value, ttl,timestamp: Date.now()});}get(key){const versionedKey =`${this.version}:${key}`;const item =this.cache.get(versionedKey);if(!item)returnnull;if(item.ttl && Date.now()- item.timestamp > item.ttl){this.delete(key);returnnull;}return item.value;}delete(key){const versionedKey =`${this.version}:${key}`;this.cache.delete(versionedKey);}clear(){this.cache.clear();}}// 使用版本控制const versionedCache =newVersionedCache();// 应用更新时升级版本 versionedCache.setVersion('v2');3. 基于依赖的失效
// 基于依赖图的缓存失效classDependencyCache{constructor(){this.cache =newMap();this.dependents =newMap();// key -> Set<dependent keys>}set(key, value, dependencies =[]){// 设置缓存this.cache.set(key, value);// 更新依赖关系 dependencies.forEach(dep=>{if(!this.dependents.has(dep)){this.dependents.set(dep,newSet());}this.dependents.get(dep).add(key);});}invalidate(key){// 删除缓存this.cache.delete(key);// 删除依赖它的缓存const deps =this.dependents.get(key);if(deps){ deps.forEach(depKey=>{this.cache.delete(depKey);});this.dependents.delete(key);}}get(key){returnthis.cache.get(key);}}// 使用示例const depCache =newDependencyCache();// 设置缓存和依赖关系 depCache.set('user:123',{name:'张三'},['settings:123']); depCache.set('settings:123',{theme:'dark'});// settings 变化时,user:123 也会失效 depCache.invalidate('settings:123');4. 手动失效
// 手动失效策略classCacheInvalidator{constructor(){this.patterns =newMap();// pattern -> handler}// 注册失效模式register(pattern, handler){this.patterns.set(pattern, handler);}// 失效缓存asyncinvalidate(cacheKey){for(const[pattern, handler]ofthis.patterns.entries()){if(this.matchPattern(pattern, cacheKey)){awaithandler(cacheKey);}}}matchPattern(pattern, key){// 简单的 glob 模式匹配const regex =newRegExp(pattern.replace('*','.*'));return regex.test(key);}}// 配置失效规则const invalidator =newCacheInvalidator(); invalidator.register('user:*',(key)=>{ console.log('失效用户缓存:', key); cache.delete(key);}); invalidator.register('product:*',(key)=>{ console.log('失效商品缓存:', key); cache.delete(key);});// 使用await invalidator.invalidate('user:123');常见问题与解决方案
1. 缓存穿透
// 问题:请求不存在的数据,导致缓存失效// 解决:缓存空值或使用布隆过滤器classAntiPenetrationCache{constructor(cache){this.cache = cache;this.nullCache =newSet();// 缓存空值的 keythis.nullTTL =5*60*1000;// 空值缓存5分钟}asyncget(key){// 检查是否是已知不存在的 keyif(this.nullCache.has(key)){returnnull;}const value =awaitthis.cache.get(key);// 如果数据不存在,缓存空值if(value ===null){this.nullCache.add(key);setTimeout(()=>{this.nullCache.delete(key);},this.nullTTL);}return value;}asyncset(key, value){if(value ===null){this.nullCache.add(key);}else{this.nullCache.delete(key);}awaitthis.cache.set(key, value);}}2. 缓存雪崩
// 问题:大量缓存同时失效// 解决:添加随机延迟、使用分布式锁classAvalancheProtectionCache{constructor(cache){this.cache = cache;this.locks =newSet();}asyncget(key, fetcher, ttl =300000){const value =awaitthis.cache.get(key);if(value !==null){return value;}// 检查是否已经有其他请求在加载if(this.locks.has(key)){// 等待其他请求完成returnnewPromise((resolve)=>{const checkLock =setInterval(()=>{if(!this.locks.has(key)){clearInterval(checkLock);this.get(key, fetcher, ttl).then(resolve);}},100);});}// 获取分布式锁this.locks.add(key);try{const data =awaitfetcher();// 添加随机 TTL 避免同时失效const randomTTL = ttl + Math.random()* ttl *0.1;// 最多额外10%awaitthis.cache.set(key, data, randomTTL);return data;}finally{this.locks.delete(key);}}}3. 缓存击穿
// 问题:热点数据失效时大量请求直达数据库// 解决:互斥锁classCacheBreakdownProtection{constructor(cache){this.cache = cache;this.locks =newMap();}asyncget(key, fetcher, ttl =300000){let value =awaitthis.cache.get(key);if(value !==null){return value;}// 获取锁(使用分布式锁更好)const lockKey =`lock:${key}`;const lockValue = Date.now().toString();if(this.locks.has(lockKey)){// 等待锁释放returnnewPromise((resolve, reject)=>{const timeout =setTimeout(()=>{this.locks.delete(lockKey);reject(newError('Lock timeout'));},5000);const checkLock =setInterval(()=>{if(!this.locks.has(lockKey)){clearInterval(checkLock);clearTimeout(timeout);this.get(key, fetcher, ttl).then(resolve).catch(reject);}},100);});}// 加锁this.locks.set(lockKey, lockValue);try{const data =awaitfetcher();awaitthis.cache.set(key, data, ttl);return data;}finally{// 释放锁this.locks.delete(lockKey);}}}4. 数据一致性
// 数据一致性保证classConsistentCache{constructor(cache){this.cache = cache;this.versionMap =newMap();// key -> version}asyncset(key, value, ttl =0){const version = Date.now();const data ={ value, version };awaitthis.cache.set(key, data, ttl);this.versionMap.set(key, version);}asyncget(key){const data =awaitthis.cache.get(key);if(!data)returnnull;// 检查版本是否是最新的const currentVersion =this.versionMap.get(key);if(data.version !== currentVersion){returnnull;// 版本过期}return data.value;}// 标记数据为已更新invalidate(key){this.versionMap.set(key, Date.now());}}最佳实践
1. 缓存设计原则
// 缓存设计检查清单constCACHE_CHECKLIST={// 1. 明确缓存对象identifyCacheTargets:()=>{// - 静态资源(CSS、JS、图片)// - API 数据(用户信息、列表数据)// - 计算结果(复杂计算、排序、筛选)// - 页面组件(React/Vue 组件)},// 2. 选择合适策略selectStrategy:()=>{// - 静态资源:Cache First// - 频繁更新:Network First// - 展示优先:Stale While Revalidate// - 敏感数据:Network Only},// 3. 设置合理 TTLsetTTL:()=>{// - 用户信息:5-10分钟// - 静态资源:1年// - 配置数据:1小时// - 列表数据:1-5分钟},// 4. 缓存失效策略invalidationStrategy:()=>{// - 基于时间的失效// - 基于版本的失效// - 基于事件的失效// - 手动失效},// 5. 监控和统计monitoring:()=>{// - 命中率统计// - 缓存大小监控// - 性能指标// - 错误率统计}};2. 缓存最佳实践
// 缓存实践指南classCacheBestPractices{// 1. 缓存键命名规范staticgenerateCacheKey(base, params){return`${base}:${JSON.stringify(params)}`;}// 2. 缓存数据大小限制staticisCacheableSize(size, maxSize =1024*1024){return size <= maxSize;}// 3. 压缩大数据staticasynccompress(data){if(typeof data ==='string'){return LZString.compress(data);}returnJSON.stringify(data);}// 4. 批量操作优化staticasyncbatchGet(cache, keys){const promises = keys.map(key=> cache.get(key));return Promise.all(promises);}staticasyncbatchSet(cache, items){const promises = items.map(({ key, value, ttl })=> cache.set(key, value, ttl));return Promise.all(promises);}// 5. 预加载常用数据staticasyncpreload(cache, configs){const promises = configs.map(({ key, fetcher, ttl })=> cache.get(key).then(value=>{if(value)return value;returnfetcher().then(data=>{ cache.set(key, data, ttl);return data;});}));return Promise.all(promises);}}3. 性能优化技巧
// 性能优化技巧classCachePerformanceOptimization{// 1. 异步缓存staticasyncasyncCache(cache, key, fetcher, ttl){const cached = cache.get(key);if(cached)return cached;// 启动后台加载fetcher().then(data=>{ cache.set(key, data, ttl);}).catch(error=>{ console.error('Async cache load failed:', error);});// 返回占位数据或 nullreturnnull;}// 2. 缓存预取staticprefetch(cache, urls){ urls.forEach(url=>{// 空闲时预取if('requestIdleCallback'in window){requestIdleCallback(()=>{ cache.get(url);});}else{setTimeout(()=> cache.get(url),0);}});}// 3. 缓存分片staticshardCache(cache, shardCount =10){const shardedCaches = Array.from({length: shardCount },()=>newMap());return{get(key){const shardIndex =this.getShardIndex(key);return shardedCaches[shardIndex].get(key);},set(key, value, ttl){const shardIndex =this.getShardIndex(key); shardedCaches[shardIndex].set(key, value);},getShardIndex(key){let hash =0;for(let i =0; i < key.length; i++){ hash =(hash <<5)- hash + key.charCodeAt(i); hash |=0;}return Math.abs(hash)% shardCount;}};}// 4. 缓存统计和优化建议staticanalyzeCache(cache){const stats = cache.getStats();const suggestions =[];if(stats.hitRate <0.8){ suggestions.push('命中率低于80%,建议调整缓存策略');}if(stats.size >100){ suggestions.push('缓存项过多,考虑使用 LRU 策略');}return{ stats, suggestions };}}4. 安全性考虑
// 缓存安全classSecureCache{constructor(cache){this.cache = cache;}// 1. 加密敏感数据asyncsetSecure(key, value, password, ttl =0){const encrypted =awaitthis.encrypt(value, password);awaitthis.cache.set(key, encrypted, ttl);}asyncgetSecure(key, password){const encrypted =awaitthis.cache.get(key);if(!encrypted)returnnull;returnawaitthis.decrypt(encrypted, password);}asyncencrypt(data, password){// 使用 Web Crypto API 加密const encoder =newTextEncoder();const dataBuffer = encoder.encode(JSON.stringify(data));const passwordBuffer = encoder.encode(password);// 导入密钥const key =await crypto.subtle.importKey('raw', passwordBuffer,{name:'PBKDF2'},false,['deriveKey']);// 生成盐const salt = crypto.getRandomValues(newUint8Array(16));// 派生密钥const derivedKey =await crypto.subtle.deriveKey({name:'PBKDF2',salt: salt,iterations:100000,hash:'SHA-256'}, key,{name:'AES-GCM',length:256},false,['encrypt']);// 加密数据const iv = crypto.getRandomValues(newUint8Array(12));const encryptedBuffer =await crypto.subtle.encrypt({name:'AES-GCM',iv: iv }, derivedKey, dataBuffer );// 返回加密后的数据(包含盐和 IV)return{salt: Array.from(salt),iv: Array.from(iv),data: Array.from(newUint8Array(encryptedBuffer))};}asyncdecrypt(encryptedData, password){// 解密实现const{ salt, iv, data }= encryptedData;// ... 解密逻辑returnJSON.parse(decryptedString);}// 2. 防止缓存投毒validateCacheData(data){// 验证数据结构if(typeof data !=='object'|| data ===null){returnfalse;}// 检查敏感字段if(data.password || data.token || data.secret){ console.warn('Sensitive data in cache');returnfalse;}returntrue;}// 3. 访问控制asyncgetWithAccessCheck(key, accessToken){// 验证访问令牌if(!this.validateAccessToken(accessToken)){thrownewError('Access denied');}const data =awaitthis.cache.get(key);if(!this.validateCacheData(data)){thrownewError('Invalid cache data');}return data;}}总结
缓存技术总结
缓存是前端性能优化的核心技术之一,它通过在多个层级存储数据副本来提高访问速度和减少网络请求。
主要缓存技术
- HTTP 缓存:浏览器层面的缓存,控制静态资源的缓存行为
- Service Worker 缓存:强大的缓存控制能力,支持离线应用
- 内存缓存:最快的缓存方式,适用于临时数据
- 状态管理缓存:应用层面的数据缓存,提升用户体验
- 浏览器存储缓存:本地持久化存储,跨会话数据共享
关键策略
- Cache First:适用于静态资源
- Network First:适用于频繁更新的数据
- Stale While Revalidate:平衡性能和新鲜度
- Cache Only:适用于离线页面
- Network Only:适用于敏感数据
最佳实践
- 合理选择缓存层级:多层缓存架构
- 设置合适的 TTL:根据数据特性调整过期时间
- 监控缓存性能:跟踪命中率和性能指标
- 处理缓存问题:防止穿透、雪崩、击穿
- 保障数据安全:加密敏感数据,验证缓存内容