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

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

目录

  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. 保障数据安全:加密敏感数据,验证缓存内容
相关推荐
恋猫de小郭35 分钟前
Flutter Zero 是什么?它的出现有什么意义?为什么你需要了解下?
android·前端·flutter
崔庆才丨静觅7 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
陌上丨8 小时前
Redis的Key和Value的设计原则有哪些?
数据库·redis·缓存
passerby60618 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了8 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅8 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅8 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅9 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment9 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅9 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端