前端缓存全解析:提升性能的关键策略

前端缓存技术深度解析:从原理到实践

目录

  1. 缓存概述
  2. [HTTP 缓存(浏览器缓存)](#HTTP 缓存(浏览器缓存))
    • [2.1 强缓存](#2.1 强缓存)
    • [2.2 协商缓存](#2.2 协商缓存)
    • [2.3 缓存控制指令](#2.3 缓存控制指令)
  3. [Service Worker 缓存](#Service Worker 缓存)
    • [3.1 Cache Storage](#3.1 Cache Storage)
    • [3.2 缓存策略](#3.2 缓存策略)
    • [3.3 离线优先](#3.3 离线优先)
  4. 内存缓存
    • [4.1 内存数据结构](#4.1 内存数据结构)
    • [4.2 LRU 缓存实现](#4.2 LRU 缓存实现)
    • [4.3 内存缓存管理](#4.3 内存缓存管理)
  5. 状态管理缓存
    • [5.1 Redux 缓存](#5.1 Redux 缓存)
    • [5.2 Vuex/Pinia 缓存](#5.2 Vuex/Pinia 缓存)
    • [5.3 React Query 缓存](#5.3 React Query 缓存)
  6. 浏览器存储缓存
    • [6.1 LocalStorage](#6.1 LocalStorage)
    • [6.2 SessionStorage](#6.2 SessionStorage)
    • [6.3 IndexedDB](#6.3 IndexedDB)
  7. 缓存策略详解
    • [7.1 Cache First](#7.1 Cache First)
    • [7.2 Network First](#7.2 Network First)
    • [7.3 Stale While Revalidate](#7.3 Stale While Revalidate)
    • [7.4 Cache Only](#7.4 Cache Only)
    • [7.5 Network Only](#7.5 Network Only)
  8. 实际应用场景
    • [8.1 API 数据缓存](#8.1 API 数据缓存)
    • [8.2 图片资源缓存](#8.2 图片资源缓存)
    • [8.3 页面组件缓存](#8.3 页面组件缓存)
  9. 性能优化实践
  10. 缓存失效与更新
  11. 常见问题与解决方案
  12. 最佳实践
  13. 总结

缓存概述

什么是缓存

缓存(Cache)是一种存储数据的技术,其核心目标是通过存储数据的副本,在后续请求中直接返回这些副本,从而提高数据访问速度和系统性能。

为什么需要缓存

在 Web 应用中,缓存的重要性不言而喻:

复制代码
无缓存场景:
用户请求 → 服务器处理 → 数据库查询 → 数据处理 → 响应返回
     ↑                                              ↓
   慢(多次网络往返 + 复杂计算)              耗时:500-2000ms

有缓存场景:
用户请求 → 缓存检查 → 直接返回
     ↑                                      ↓
   快(内存访问)                         耗时:1-50ms

缓存的优势

  1. 提升性能:减少网络请求,加快页面加载速度
  2. 降低服务器负载:减少数据库查询和计算
  3. 节省带宽:减少数据传输量
  4. 改善用户体验:离线可用,快速响应
  5. 降低成本:减少服务器资源消耗

缓存的挑战

  1. 数据一致性:缓存数据可能与源数据不同步
  2. 缓存穿透:请求不存在的数据导致缓存失效
  3. 缓存雪崩:大量缓存同时失效导致请求涌入后端
  4. 缓存击穿:热点数据失效时大量请求直达数据库
  5. 存储空间限制:浏览器存储空间有限

缓存层级模型

复制代码
┌─────────────────────────────────────┐
│          用户请求                    │
└─────────────────┬───────────────────┘
                  │
┌─────────────────▼───────────────────┐
│         浏览器缓存                   │
│  ┌──────────┐ ┌──────────┐        │
│  │  HTTP    │ │ Service  │        │
│  │  缓存    │ │ Worker   │        │
│  └──────────┘ └──────────┘        │
└─────────────────┬───────────────────┘
                  │
┌─────────────────▼───────────────────┐
│         应用内存缓存                 │
│    ┌──────────────────────┐        │
│    │    React/Vue        │        │
│    │    状态缓存         │        │
│    └──────────────────────┘        │
└─────────────────┬───────────────────┘
                  │
┌─────────────────▼───────────────────┐
│         服务器缓存                   │
│    ┌──────────────────────┐        │
│    │   Redis / Memcached  │        │
│    └──────────────────────┘        │
└─────────────────┬───────────────────┘
                  │
┌─────────────────▼───────────────────┐
│         数据库缓存                   │
│    ┌──────────────────────┐        │
│    │     查询结果缓存      │        │
│    └──────────────────────┘        │
└─────────────────────────────────────┘

HTTP 缓存(浏览器缓存)

HTTP 缓存是浏览器最基础的缓存机制,通过 HTTP 头信息控制资源的缓存行为。

1. 强缓存

强缓存(Strong Cache)不会向服务器发送请求,直接从缓存中获取资源。

Cache-Control
http 复制代码
Cache-Control: max-age=3600

常用指令:

  • max-age=<seconds>:缓存有效期(秒)
  • no-cache:需要验证缓存有效性
  • no-store:完全不使用缓存
  • public:响应可被任何缓存存储
  • private:响应只能被私有缓存存储
  • immutable:资源不会更新
javascript 复制代码
// 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 复制代码
# 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
http 复制代码
Expires: Wed, 21 Oct 2026 07:28:00 GMT
javascript 复制代码
// 设置过期时间
res.set('Expires', new Date(Date.now() + 3600000).toUTCString());

注意:Expires 是 HTTP/1.0 的产物,Cache-Control 是 HTTP/1.1 的改进,优先级更高。

2. 协商缓存

协商缓存(Conditional Cache)需要向服务器验证资源是否更新。

Last-Modified / If-Modified-Since
http 复制代码
Last-Modified: Wed, 21 Oct 2026 07:28:00 GMT
javascript 复制代码
// 服务器端实现
app.get('/api/data', (req, res) => {
  const lastModified = new Date('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
http 复制代码
ETag: "abc123"
javascript 复制代码
// 服务器端实现
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. 缓存控制指令

常用指令组合
javascript 复制代码
// 不缓存
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 头
http 复制代码
Vary: Accept-Encoding
javascript 复制代码
// 根据 Accept-Encoding 缓存不同版本
res.set('Vary', 'Accept-Encoding, User-Agent');

Service Worker 缓存

Service Worker 是在浏览器后台运行的脚本,提供了强大的缓存控制能力。

1. Cache Storage

javascript 复制代码
// 注册 Service Worker
if ('serviceWorker' in navigator) {
  navigator.serviceWorker.register('/sw.js')
    .then(registration => {
      console.log('SW registered:', registration);
    });
}
javascript 复制代码
// sw.js - Service Worker 文件
const CACHE_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;
        }
        // 否则请求网络
        return fetch(event.request);
      }
    )
  );
});

2. 缓存策略

Cache First(缓存优先)
javascript 复制代码
self.addEventListener('fetch', event => {
  // 只缓存 GET 请求
  if (event.request.method !== 'GET') return;

  event.respondWith(
    caches.match(event.request)
      .then(cachedResponse => {
        if (cachedResponse) {
          return cachedResponse;
        }

        return fetch(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(网络优先)
javascript 复制代码
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(过期数据可用,同时后台更新)
javascript 复制代码
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. 离线优先

javascript 复制代码
// IndexedDB 辅助类
class IDBHelper {
  constructor(dbName, storeName) {
    this.dbName = dbName;
    this.storeName = storeName;
    this.db = null;
  }

  async init() {
    return new Promise((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' });
        }
      };
    });
  }

  async get(key) {
    return new Promise((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);
    });
  }

  async set(value) {
    return new Promise((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 = new IDBHelper('MyAppDB', 'apiCache');

self.addEventListener('fetch', event => {
  if (event.request.url.includes('/api/')) {
    event.respondWith(
      (async () => {
        try {
          // 尝试网络请求
          const networkResponse = await fetch(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) {
            return new Response(JSON.stringify(cached.data), {
              headers: { 'Content-Type': 'application/json' }
            });
          }

          // 没有缓存,返回离线页面
          return new Response('Offline', { status: 503 });
        }
      })()
    );
  }
});

内存缓存

内存缓存是在应用运行期间将数据存储在内存中的缓存方式。

1. 内存数据结构

Map 缓存
javascript 复制代码
class SimpleCache {
  constructor() {
    this.cache = new Map();
  }

  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) return null;

    // 检查是否过期
    if (item.expires && item.expires < Date.now()) {
      this.cache.delete(key);
      return null;
    }

    return item.value;
  }

  delete(key) {
    this.cache.delete(key);
  }

  clear() {
    this.cache.clear();
  }

  // 获取缓存大小
  size() {
    return this.cache.size;
  }

  // 清理过期项
  cleanup() {
    const now = Date.now();
    for (const [key, item] of this.cache.entries()) {
      if (item.expires && item.expires < now) {
        this.cache.delete(key);
      }
    }
  }
}

// 使用示例
const cache = new SimpleCache();

// 缓存用户信息,10分钟过期
cache.set('user:123', { name: '张三', age: 25 }, 10 * 60 * 1000);

const user = cache.get('user:123');
console.log(user); // { name: '张三', age: 25 }
WeakMap 缓存
javascript 复制代码
class WeakRefCache {
  constructor() {
    this.cache = new WeakMap();
    this.timeouts = new WeakMap();
  }

  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 = new WeakRefCache();

// 缓存 DOM 元素相关数据
const element = document.querySelector('#myElement');
weakCache.set(element, { data: 'some data' }, 5 * 60 * 1000);

2. LRU 缓存实现

javascript 复制代码
class LRUCache {
  constructor(limit = 100) {
    this.limit = limit;
    this.cache = new Map();
  }

  get(key) {
    if (!this.cache.has(key)) return null;

    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);
    } else if (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) return false;

    if (item.expires && item.expires < Date.now()) {
      this.cache.delete(key);
      return false;
    }

    return true;
  }

  delete(key) {
    this.cache.delete(key);
  }

  clear() {
    this.cache.clear();
  }

  // 获取所有有效项
  getValidItems() {
    const now = Date.now();
    const validItems = [];

    for (const [key, item] of this.cache.entries()) {
      if (!item.expires || item.expires >= now) {
        validItems.push({ key, value: item.value });
      } else {
        this.cache.delete(key);
      }
    }

    return validItems;
  }
}

// 使用示例
const lruCache = new LRUCache(50);

// 缓存 API 响应
async function fetchWithCache(url, ttl = 5 * 60 * 1000) {
  if (lruCache.has(url)) {
    console.log('使用缓存:', url);
    return lruCache.get(url);
  }

  const response = await fetch(url);
  const data = await response.json();

  lruCache.set(url, data, ttl);

  return data;
}

3. 内存缓存管理

缓存管理器
javascript 复制代码
class CacheManager {
  constructor() {
    this.caches = new Map();
    this.cleanupInterval = null;
  }

  createCache(name, options = {}) {
    const { limit = 100, ttl = 0 } = options;

    if (this.caches.has(name)) {
      return this.caches.get(name);
    }

    const cache = new LRUCache(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) {
    return this.caches.get(name);
  }

  deleteCache(name) {
    const cache = this.caches.get(name);
    if (cache) {
      cache.clear();
      this.caches.delete(name);
    }
  }

  clearAll() {
    for (const cache of this.caches.values()) {
      cache.clear();
    }
  }

  getAllStats() {
    const stats = [];
    for (const cache of this.caches.values()) {
      stats.push(cache.getStats());
    }
    return stats;
  }

  // 启动定期清理
  startCleanup(interval = 60000) {
    if (this.cleanupInterval) return;

    this.cleanupInterval = setInterval(() => {
      for (const cache of this.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 = new CacheManager();

// 创建不同类型的缓存
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 缓存

javascript 复制代码
// 使用 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'], // 只持久化这些 reducer
  blacklist: ['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';

function UserProfile() {
  const user = useSelector((state) => state.user.data);
  const isLoading = useSelector((state) => state.user.loading);

  if (isLoading) return <div>加载中...</div>;

  return <div>{user.name}</div>;
}
自定义中间件缓存
javascript 复制代码
// API 缓存中间件
const apiCacheMiddleware = (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 缓存

javascript 复制代码
// Pinia 缓存插件
export function cachePlugin({ 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.js
import { createPinia } from 'pinia';
import { cachePlugin } from './plugins/cache';

const pinia = createPinia();
pinia.use(cachePlugin);

app.use(pinia);
Pinia 缓存 Store
javascript 复制代码
// stores/cache.js
import { defineStore } from 'pinia';
import axios from 'axios';

export const useCacheStore = defineStore('cache', {
  state: () => ({
    _cache: new Map(),
    _timestamps: new Map()
  }),

  actions: {
    async get(key, fetcher, ttl = 300000) {
      // 检查缓存是否存在且未过期
      if (this._cache.has(key)) {
        const timestamp = this._timestamps.get(key);
        if (Date.now() - timestamp < ttl) {
          return this._cache.get(key);
        }
      }

      // 获取新数据
      const data = await fetcher();
      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();
    }
  }
});

// 使用缓存 Store
const cacheStore = useCacheStore();

const userData = await cacheStore.get('user:123', async () => {
  const response = await axios.get('/api/user/123');
  return response.data;
});

3. React Query 缓存

javascript 复制代码
// React Query 配置
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';

const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      staleTime: 5 * 60 * 1000, // 5分钟内数据新鲜
      cacheTime: 10 * 60 * 1000, // 10分钟后清理缓存
      retry: 3,
      refetchOnWindowFocus: false
    }
  }
});

function App() {
  return (
    <QueryClientProvider client={queryClient}>
      <UserProfile />
    </QueryClientProvider>
  );
}

// 使用 React Query
import { useQuery } from '@tanstack/react-query';

function UserProfile({ userId }) {
  const { data, isLoading, error } = useQuery(
    ['user', userId],
    async () => {
      const response = await fetch(`/api/user/${userId}`);
      if (!response.ok) throw new Error('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>;
}

// 手动操作缓存
function updateUser() {
  queryClient.setQueryData(['user', userId], (oldData) => ({
    ...oldData,
    name: '新名称'
  }));

  // 失效缓存
  queryClient.invalidateQueries(['user']);
}

浏览器存储缓存

1. LocalStorage

javascript 复制代码
// LocalStorage 封装类
class LocalStorageCache {
  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) return null;

    try {
      const data = JSON.parse(item);

      // 检查是否过期
      if (data.ttl && Date.now() - data.timestamp > data.ttl) {
        this.delete(key);
        return null;
      }

      return data.value;
    } catch (error) {
      console.error('Parse error:', error);
      this.delete(key);
      return null;
    }
  }

  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 = new LocalStorageCache();

// 缓存用户设置
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

javascript 复制代码
// SessionStorage 缓存
class SessionStorageCache {
  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) return null;

    try {
      const data = JSON.parse(item);
      return data.value;
    } catch (error) {
      console.error('Parse error:', error);
      this.delete(key);
      return null;
    }
  }

  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 = new SessionStorageCache();

// 缓存表单数据
sessionCache.set('formData', {
  name: '张三',
  email: 'zhangsan@example.com'
});

// 页面刷新后数据仍然存在(会话期间)
const formData = sessionCache.get('formData');

3. IndexedDB

javascript 复制代码
// IndexedDB 封装
class IndexedDBCache {
  constructor(dbName, storeName) {
    this.dbName = dbName;
    this.storeName = storeName;
    this.db = null;
  }

  async init() {
    return new Promise((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 });
        }
      };
    });
  }

  async set(key, value, ttl = 0) {
    const data = {
      key,
      value,
      timestamp: Date.now(),
      ttl
    };

    return new Promise((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);
    });
  }

  async get(key) {
    return new Promise((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);
      };
    });
  }

  async delete(key) {
    return new Promise((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);
    });
  }

  async clear() {
    return new Promise((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);
    });
  }

  async getAll() {
    return new Promise((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 = new IndexedDBCache('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

javascript 复制代码
// Cache First 策略:先查缓存,缓存没有再请求网络
async function cacheFirst(request, cacheName) {
  const cache = await caches.open(cacheName);
  const cachedResponse = await cache.match(request);

  if (cachedResponse) {
    return cachedResponse;
  }

  const networkResponse = await fetch(request);

  if (networkResponse.ok) {
    cache.put(request, networkResponse.clone());
  }

  return networkResponse;
}

// 适用场景:静态资源(CSS、JS、图片)
const cssResponse = await cacheFirst('/static/css/main.css', 'assets-cache');

2. Network First

javascript 复制代码
// Network First 策略:先请求网络,失败则使用缓存
async function networkFirst(request, cacheName, timeout = 5000) {
  const cache = await caches.open(cacheName);

  try {
    // 设置超时
    const networkPromise = fetch(request);
    const timeoutPromise = new Promise((resolve, reject) => {
      setTimeout(() => reject(new Error('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 = await networkFirst('/api/user/123', 'api-cache');

3. Stale While Revalidate

javascript 复制代码
// Stale While Revalidate 策略:返回缓存,同时后台更新
async function staleWhileRevalidate(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);
      return null;
    });

  // 立即返回缓存,同时后台更新
  return cachedResponse || fetchPromise;
}

// 适用场景:新闻文章、博客内容
const articles = await staleWhileRevalidate('/api/articles', 'articles-cache');

4. Cache Only

javascript 复制代码
// Cache Only 策略:只从缓存获取
async function cacheOnly(request, cacheName) {
  const cache = await caches.open(cacheName);
  const cachedResponse = await cache.match(request);

  if (!cachedResponse) {
    throw new Error('No cached response found');
  }

  return cachedResponse;
}

// 适用场景:离线页面
const offlinePage = await cacheOnly('/offline.html', 'offline-cache');

5. Network Only

javascript 复制代码
// Network Only 策略:只从网络获取
async function networkOnly(request) {
  return await fetch(request);
}

// 适用场景:登录、支付等敏感操作
const loginResponse = await networkOnly('/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 数据缓存

用户信息缓存
javascript 复制代码
class UserCache {
  constructor() {
    this.cache = new LRUCache(50);
    this.apiCache = new LocalStorageCache('user_');
  }

  async getUser(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;
    }

    // 请求 API
    try {
      const response = await fetch(`/api/user/${userId}`);
      if (!response.ok) throw new Error('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 = new UserCache();

// 使用
const user = await userCache.getUser('123');
userCache.updateUser('123', { ...user, name: '新名称' });
分页数据缓存
javascript 复制代码
class PaginatedCache {
  constructor() {
    this.cache = new Map(); // key: `${url}:${page}`, value: { data, timestamp }
    this.cacheTime = 5 * 60 * 1000; // 5分钟
  }

  async getPage(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 = new URLSearchParams({ page, ...params });
    const response = await fetch(`${url}?${queryParams}`);

    if (!response.ok) throw new Error('Failed to fetch');

    const data = await response.json();

    this.cache.set(cacheKey, {
      data,
      timestamp: Date.now()
    });

    return data;
  }

  invalidatePage(url, page) {
    // 删除指定页的缓存
    for (const key of this.cache.keys()) {
      if (key.startsWith(`${url}:${page}:`)) {
        this.cache.delete(key);
      }
    }
  }

  invalidateUrl(url) {
    // 删除指定 URL 的所有缓存
    for (const key of this.cache.keys()) {
      if (key.startsWith(`${url}:`)) {
        this.cache.delete(key);
      }
    }
  }
}

const paginatedCache = new PaginatedCache();

// 使用
const products = await paginatedCache.getPage('/api/products', 1, {
  category: 'electronics',
  sort: 'price'
});

2. 图片资源缓存

图片预加载
javascript 复制代码
class ImageCache {
  constructor(maxCache = 50) {
    this.cache = new Map(); // key: url, value: { img, timestamp }
    this.maxCache = maxCache;
  }

  preloadImage(url) {
    return new Promise((resolve, reject) => {
      // 检查缓存
      const cached = this.cache.get(url);
      if (cached) {
        resolve(cached.img);
        return;
      }

      const img = new Image();
      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 = new ImageCache();

// 预加载图片
function preloadImages(urls) {
  return Promise.all(urls.map(url => imageCache.preloadImage(url)));
}

// 使用
preloadImages([
  '/images/hero.jpg',
  '/images/product1.jpg',
  '/images/product2.jpg'
]).then(() => {
  console.log('所有图片预加载完成');
});
图片懒加载
javascript 复制代码
class LazyImageLoader {
  constructor() {
    this.imageCache = new ImageCache(100);
    this.observer = null;
    this.init();
  }

  init() {
    // Intersection Observer 实现懒加载
    if ('IntersectionObserver' in window) {
      this.observer = new IntersectionObserver((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开始加载
      });
    }
  }

  async loadImage(img, src) {
    try {
      const image = await this.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 = new LazyImageLoader();

// HTML
// <img class="lazy" data-src="/images/large-image.jpg" alt="示例">
// <img class="lazy" data-src="/images/another-image.jpg" alt="示例">

// JavaScript
document.querySelectorAll('img.lazy').forEach(img => {
  lazyImageLoader.observe(img);
});

3. 页面组件缓存

React 组件缓存
javascript 复制代码
// 使用 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 缓存函数
function ParentComponent() {
  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>
  );
}
动态组件缓存
javascript 复制代码
class ComponentCache {
  constructor() {
    this.cache = new Map();
  }

  async loadComponent(importFunc) {
    const modulePath = importFunc.toString();

    // 检查缓存
    if (this.cache.has(modulePath)) {
      return this.cache.get(modulePath);
    }

    // 动态加载组件
    const module = await importFunc();
    const Component = module.default;

    // 缓存组件
    this.cache.set(modulePath, Component);

    return Component;
  }

  clear() {
    this.cache.clear();
  }
}

const componentCache = new ComponentCache();

// 动态加载路由组件
const routes = [
  {
    path: '/user',
    component: () => import('./UserComponent')
  },
  {
    path: '/product',
    component: () => import('./ProductComponent')
  }
];

// 渲染路由
function renderRoute(route) {
  componentCache.loadComponent(route.component)
    .then(Component => {
      ReactDOM.render(<Component />, document.getElementById('root'));
    });
}

性能优化实践

1. 缓存分层策略

javascript 复制代码
// 多层缓存架构
class MultiLayerCache {
  constructor() {
    this.l1Cache = new Map(); // 内存缓存,最快
    this.l2Cache = new LocalStorageCache('l2_'); // 本地存储
    this.l3Cache = new IndexedDBCache('MyApp', 'l3'); // IndexedDB
  }

  async get(key) {
    // L1 缓存
    if (this.l1Cache.has(key)) {
      return this.l1Cache.get(key);
    }

    // L2 缓存
    const l2Data = this.l2Cache.get(key);
    if (l2Data) {
      this.l1Cache.set(key, l2Data);
      return l2Data;
    }

    // L3 缓存
    const l3Data = await this.l3Cache.get(key);
    if (l3Data) {
      this.l1Cache.set(key, l3Data);
      this.l2Cache.set(key, l3Data);
      return l3Data;
    }

    return null;
  }

  async set(key, value, ttl = 0) {
    // 设置所有层级
    this.l1Cache.set(key, value);
    this.l2Cache.set(key, value, ttl);
    await this.l3Cache.set(key, value, ttl);
  }

  async delete(key) {
    this.l1Cache.delete(key);
    this.l2Cache.delete(key);
    await this.l3Cache.delete(key);
  }

  clear() {
    this.l1Cache.clear();
    this.l2Cache.clear();
    this.l3Cache.clear();
  }
}

const multiLayerCache = new MultiLayerCache();

2. 缓存预热

javascript 复制代码
// 缓存预热
class CacheWarmer {
  constructor(cache) {
    this.cache = cache;
  }

  async warmUp(config) {
    console.log('开始缓存预热...');

    const promises = config.map(async ({ key, fetcher, ttl }) => {
      try {
        const data = await fetcher();
        await this.cache.set(key, data, ttl);
        console.log(`预热成功: ${key}`);
      } catch (error) {
        console.error(`预热失败: ${key}`, error);
      }
    });

    await Promise.all(promises);
    console.log('缓存预热完成');
  }
}

// 配置预热数据
const warmer = new CacheWarmer(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. 缓存压缩

javascript 复制代码
// 缓存数据压缩
import LZString from 'lz-string';

class CompressedCache {
  constructor(cache) {
    this.cache = cache;
  }

  async set(key, value, ttl = 0) {
    // 压缩数据
    const compressed = LZString.compress(JSON.stringify(value));
    await this.cache.set(key, compressed, ttl);
  }

  async get(key) {
    const compressed = await this.cache.get(key);
    if (!compressed) return null;

    try {
      const decompressed = LZString.decompress(compressed);
      return JSON.parse(decompressed);
    } catch (error) {
      console.error('Failed to decompress:', error);
      return null;
    }
  }
}

const compressedCache = new CompressedCache(multiLayerCache);

4. 缓存统计

javascript 复制代码
// 缓存统计
class CacheStats {
  constructor() {
    this.hits = 0;
    this.misses = 0;
    this.size = 0;
  }

  recordHit() {
    this.hits++;
  }

  recordMiss() {
    this.misses++;
  }

  get hitRate() {
    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;
  }
}

// 带统计的缓存
class StatsCache {
  constructor(cache) {
    this.cache = cache;
    this.stats = new CacheStats();
  }

  async get(key) {
    const value = await this.cache.get(key);
    if (value) {
      this.stats.recordHit();
    } else {
      this.stats.recordMiss();
    }
    return value;
  }

  async set(key, value, ttl) {
    return this.cache.set(key, value, ttl);
  }

  getStats() {
    return this.stats.getStats();
  }

  resetStats() {
    this.stats.reset();
  }
}

const statsCache = new StatsCache(multiLayerCache);

// 定期输出统计信息
setInterval(() => {
  console.log('缓存统计:', statsCache.getStats());
}, 60000);

缓存失效与更新

1. TTL(Time To Live)

javascript 复制代码
// TTL 管理
class TTLCache {
  constructor() {
    this.cache = new Map();
    this.timeouts = new Map();
  }

  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) {
    return this.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 of this.timeouts.values()) {
      clearTimeout(timeoutId);
    }
    this.timeouts.clear();
    this.cache.clear();
  }
}

2. 版本控制

javascript 复制代码
// 基于版本的缓存失效
class VersionedCache {
  constructor() {
    this.cache = new Map();
    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) return null;

    if (item.ttl && Date.now() - item.timestamp > item.ttl) {
      this.delete(key);
      return null;
    }

    return item.value;
  }

  delete(key) {
    const versionedKey = `${this.version}:${key}`;
    this.cache.delete(versionedKey);
  }

  clear() {
    this.cache.clear();
  }
}

// 使用版本控制
const versionedCache = new VersionedCache();

// 应用更新时升级版本
versionedCache.setVersion('v2');

3. 基于依赖的失效

javascript 复制代码
// 基于依赖图的缓存失效
class DependencyCache {
  constructor() {
    this.cache = new Map();
    this.dependents = new Map(); // 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, new Set());
      }
      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) {
    return this.cache.get(key);
  }
}

// 使用示例
const depCache = new DependencyCache();

// 设置缓存和依赖关系
depCache.set('user:123', { name: '张三' }, ['settings:123']);
depCache.set('settings:123', { theme: 'dark' });

// settings 变化时,user:123 也会失效
depCache.invalidate('settings:123');

4. 手动失效

javascript 复制代码
// 手动失效策略
class CacheInvalidator {
  constructor() {
    this.patterns = new Map(); // pattern -> handler
  }

  // 注册失效模式
  register(pattern, handler) {
    this.patterns.set(pattern, handler);
  }

  // 失效缓存
  async invalidate(cacheKey) {
    for (const [pattern, handler] of this.patterns.entries()) {
      if (this.matchPattern(pattern, cacheKey)) {
        await handler(cacheKey);
      }
    }
  }

  matchPattern(pattern, key) {
    // 简单的 glob 模式匹配
    const regex = new RegExp(pattern.replace('*', '.*'));
    return regex.test(key);
  }
}

// 配置失效规则
const invalidator = new CacheInvalidator();

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. 缓存穿透

javascript 复制代码
// 问题:请求不存在的数据,导致缓存失效
// 解决:缓存空值或使用布隆过滤器

class AntiPenetrationCache {
  constructor(cache) {
    this.cache = cache;
    this.nullCache = new Set(); // 缓存空值的 key
    this.nullTTL = 5 * 60 * 1000; // 空值缓存5分钟
  }

  async get(key) {
    // 检查是否是已知不存在的 key
    if (this.nullCache.has(key)) {
      return null;
    }

    const value = await this.cache.get(key);

    // 如果数据不存在,缓存空值
    if (value === null) {
      this.nullCache.add(key);
      setTimeout(() => {
        this.nullCache.delete(key);
      }, this.nullTTL);
    }

    return value;
  }

  async set(key, value) {
    if (value === null) {
      this.nullCache.add(key);
    } else {
      this.nullCache.delete(key);
    }
    await this.cache.set(key, value);
  }
}

2. 缓存雪崩

javascript 复制代码
// 问题:大量缓存同时失效
// 解决:添加随机延迟、使用分布式锁

class AvalancheProtectionCache {
  constructor(cache) {
    this.cache = cache;
    this.locks = new Set();
  }

  async get(key, fetcher, ttl = 300000) {
    const value = await this.cache.get(key);

    if (value !== null) {
      return value;
    }

    // 检查是否已经有其他请求在加载
    if (this.locks.has(key)) {
      // 等待其他请求完成
      return new Promise((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 = await fetcher();

      // 添加随机 TTL 避免同时失效
      const randomTTL = ttl + Math.random() * ttl * 0.1; // 最多额外10%
      await this.cache.set(key, data, randomTTL);

      return data;
    } finally {
      this.locks.delete(key);
    }
  }
}

3. 缓存击穿

javascript 复制代码
// 问题:热点数据失效时大量请求直达数据库
// 解决:互斥锁

class CacheBreakdownProtection {
  constructor(cache) {
    this.cache = cache;
    this.locks = new Map();
  }

  async get(key, fetcher, ttl = 300000) {
    let value = await this.cache.get(key);

    if (value !== null) {
      return value;
    }

    // 获取锁(使用分布式锁更好)
    const lockKey = `lock:${key}`;
    const lockValue = Date.now().toString();

    if (this.locks.has(lockKey)) {
      // 等待锁释放
      return new Promise((resolve, reject) => {
        const timeout = setTimeout(() => {
          this.locks.delete(lockKey);
          reject(new Error('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 = await fetcher();
      await this.cache.set(key, data, ttl);
      return data;
    } finally {
      // 释放锁
      this.locks.delete(lockKey);
    }
  }
}

4. 数据一致性

javascript 复制代码
// 数据一致性保证
class ConsistentCache {
  constructor(cache) {
    this.cache = cache;
    this.versionMap = new Map(); // key -> version
  }

  async set(key, value, ttl = 0) {
    const version = Date.now();
    const data = { value, version };
    await this.cache.set(key, data, ttl);
    this.versionMap.set(key, version);
  }

  async get(key) {
    const data = await this.cache.get(key);
    if (!data) return null;

    // 检查版本是否是最新的
    const currentVersion = this.versionMap.get(key);
    if (data.version !== currentVersion) {
      return null; // 版本过期
    }

    return data.value;
  }

  // 标记数据为已更新
  invalidate(key) {
    this.versionMap.set(key, Date.now());
  }
}

最佳实践

1. 缓存设计原则

javascript 复制代码
// 缓存设计检查清单
const CACHE_CHECKLIST = {
  // 1. 明确缓存对象
  identifyCacheTargets: () => {
    // - 静态资源(CSS、JS、图片)
    // - API 数据(用户信息、列表数据)
    // - 计算结果(复杂计算、排序、筛选)
    // - 页面组件(React/Vue 组件)
  },

  // 2. 选择合适策略
  selectStrategy: () => {
    // - 静态资源:Cache First
    // - 频繁更新:Network First
    // - 展示优先:Stale While Revalidate
    // - 敏感数据:Network Only
  },

  // 3. 设置合理 TTL
  setTTL: () => {
    // - 用户信息:5-10分钟
    // - 静态资源:1年
    // - 配置数据:1小时
    // - 列表数据:1-5分钟
  },

  // 4. 缓存失效策略
  invalidationStrategy: () => {
    // - 基于时间的失效
    // - 基于版本的失效
    // - 基于事件的失效
    // - 手动失效
  },

  // 5. 监控和统计
  monitoring: () => {
    // - 命中率统计
    // - 缓存大小监控
    // - 性能指标
    // - 错误率统计
  }
};

2. 缓存最佳实践

javascript 复制代码
// 缓存实践指南
class CacheBestPractices {
  // 1. 缓存键命名规范
  static generateCacheKey(base, params) {
    return `${base}:${JSON.stringify(params)}`;
  }

  // 2. 缓存数据大小限制
  static isCacheableSize(size, maxSize = 1024 * 1024) {
    return size <= maxSize;
  }

  // 3. 压缩大数据
  static async compress(data) {
    if (typeof data === 'string') {
      return LZString.compress(data);
    }
    return JSON.stringify(data);
  }

  // 4. 批量操作优化
  static async batchGet(cache, keys) {
    const promises = keys.map(key => cache.get(key));
    return Promise.all(promises);
  }

  static async batchSet(cache, items) {
    const promises = items.map(({ key, value, ttl }) =>
      cache.set(key, value, ttl)
    );
    return Promise.all(promises);
  }

  // 5. 预加载常用数据
  static async preload(cache, configs) {
    const promises = configs.map(({ key, fetcher, ttl }) =>
      cache.get(key).then(value => {
        if (value) return value;
        return fetcher().then(data => {
          cache.set(key, data, ttl);
          return data;
        });
      })
    );
    return Promise.all(promises);
  }
}

3. 性能优化技巧

javascript 复制代码
// 性能优化技巧
class CachePerformanceOptimization {
  // 1. 异步缓存
  static async asyncCache(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);
    });

    // 返回占位数据或 null
    return null;
  }

  // 2. 缓存预取
  static prefetch(cache, urls) {
    urls.forEach(url => {
      // 空闲时预取
      if ('requestIdleCallback' in window) {
        requestIdleCallback(() => {
          cache.get(url);
        });
      } else {
        setTimeout(() => cache.get(url), 0);
      }
    });
  }

  // 3. 缓存分片
  static shardCache(cache, shardCount = 10) {
    const shardedCaches = Array.from({ length: shardCount }, () => new Map());

    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. 缓存统计和优化建议
  static analyzeCache(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. 安全性考虑

javascript 复制代码
// 缓存安全
class SecureCache {
  constructor(cache) {
    this.cache = cache;
  }

  // 1. 加密敏感数据
  async setSecure(key, value, password, ttl = 0) {
    const encrypted = await this.encrypt(value, password);
    await this.cache.set(key, encrypted, ttl);
  }

  async getSecure(key, password) {
    const encrypted = await this.cache.get(key);
    if (!encrypted) return null;

    return await this.decrypt(encrypted, password);
  }

  async encrypt(data, password) {
    // 使用 Web Crypto API 加密
    const encoder = new TextEncoder();
    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(new Uint8Array(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(new Uint8Array(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(new Uint8Array(encryptedBuffer))
    };
  }

  async decrypt(encryptedData, password) {
    // 解密实现
    const { salt, iv, data } = encryptedData;
    // ... 解密逻辑
    return JSON.parse(decryptedString);
  }

  // 2. 防止缓存投毒
  validateCacheData(data) {
    // 验证数据结构
    if (typeof data !== 'object' || data === null) {
      return false;
    }

    // 检查敏感字段
    if (data.password || data.token || data.secret) {
      console.warn('Sensitive data in cache');
      return false;
    }

    return true;
  }

  // 3. 访问控制
  async getWithAccessCheck(key, accessToken) {
    // 验证访问令牌
    if (!this.validateAccessToken(accessToken)) {
      throw new Error('Access denied');
    }

    const data = await this.cache.get(key);

    if (!this.validateCacheData(data)) {
      throw new Error('Invalid cache data');
    }

    return data;
  }
}

总结

缓存技术总结

缓存是前端性能优化的核心技术之一,它通过在多个层级存储数据副本来提高访问速度和减少网络请求。

主要缓存技术
  1. HTTP 缓存:浏览器层面的缓存,控制静态资源的缓存行为
  2. Service Worker 缓存:强大的缓存控制能力,支持离线应用
  3. 内存缓存:最快的缓存方式,适用于临时数据
  4. 状态管理缓存:应用层面的数据缓存,提升用户体验
  5. 浏览器存储缓存:本地持久化存储,跨会话数据共享
关键策略
  • Cache First:适用于静态资源
  • Network First:适用于频繁更新的数据
  • Stale While Revalidate:平衡性能和新鲜度
  • Cache Only:适用于离线页面
  • Network Only:适用于敏感数据
最佳实践
  1. 合理选择缓存层级:多层缓存架构
  2. 设置合适的 TTL:根据数据特性调整过期时间
  3. 监控缓存性能:跟踪命中率和性能指标
  4. 处理缓存问题:防止穿透、雪崩、击穿
  5. 保障数据安全:加密敏感数据,验证缓存内容
相关推荐
江公望2 分钟前
VUE3中,reactive()和ref()的区别10分钟讲清楚
前端·javascript·vue.js
攀登的牵牛花5 分钟前
前端向架构突围系列 - 框架设计(二):糟糕的代码有哪些特点?
前端·架构
EndingCoder15 分钟前
函数基础:参数和返回类型
linux·前端·ubuntu·typescript
码客前端21 分钟前
理解 Flex 布局中的 flex:1 与 min-width: 0 问题
前端·css·css3
Komorebi゛22 分钟前
【CSS】圆锥渐变流光效果边框样式实现
前端·css
jmxwzy27 分钟前
Redis
数据库·redis·缓存
零叹30 分钟前
Redis热Key——大厂是怎么解决的
数据库·redis·缓存·热key
win x33 分钟前
Redis事务
数据库·redis·缓存
工藤学编程34 分钟前
零基础学AI大模型之CoT思维链和ReAct推理行动
前端·人工智能·react.js
徐同保34 分钟前
上传文件,在前端用 pdf.js 提取 上传的pdf文件中的图片
前端·javascript·pdf