Skip to content

查询缓存管理

概述

TanStack Query的缓存管理是其核心功能之一。通过智能的缓存策略,可以显著提升应用性能和用户体验。本文将深入探讨如何管理查询缓存,包括失效、更新、预取等高级技巧。

QueryClient

基础使用

jsx
import { QueryClient, useQueryClient } from '@tanstack/react-query';

// 创建QueryClient实例
const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      staleTime: 1000 * 60 * 5,   // 5分钟
      cacheTime: 1000 * 60 * 10,  // 10分钟
    },
  },
});

// 在组件中使用
function MyComponent() {
  const queryClient = useQueryClient();
  
  // 访问QueryClient的各种方法
  return <div>Component</div>;
}

获取缓存数据

jsx
function GetCachedData() {
  const queryClient = useQueryClient();
  
  const handleGetData = () => {
    // 获取查询数据
    const data = queryClient.getQueryData(['user', 123]);
    console.log('User data:', data);
    
    // 获取所有匹配的查询
    const allUsers = queryClient.getQueriesData({ queryKey: ['users'] });
    console.log('All users queries:', allUsers);
    
    // 获取查询状态
    const queryState = queryClient.getQueryState(['user', 123]);
    console.log('Query state:', queryState);
  };
  
  return <button onClick={handleGetData}>Get Cached Data</button>;
}

设置缓存数据

jsx
function SetCachedData() {
  const queryClient = useQueryClient();
  
  const handleSetData = () => {
    // 设置查询数据
    queryClient.setQueryData(['user', 123], {
      id: 123,
      name: 'John Doe',
      email: 'john@example.com',
    });
    
    // 更新函数
    queryClient.setQueryData(['user', 123], (old) => ({
      ...old,
      name: 'Jane Doe',
    }));
    
    // 设置多个查询
    queryClient.setQueriesData(
      { queryKey: ['users'] },
      (old) => [...old, newUser]
    );
  };
  
  return <button onClick={handleSetData}>Set Cached Data</button>;
}

缓存失效

invalidateQueries

jsx
function InvalidateExample() {
  const queryClient = useQueryClient();
  
  const handleInvalidate = () => {
    // 失效所有查询
    queryClient.invalidateQueries();
    
    // 失效特定key的查询
    queryClient.invalidateQueries({ queryKey: ['users'] });
    
    // 精确匹配
    queryClient.invalidateQueries({
      queryKey: ['users'],
      exact: true,
    });
    
    // 使用refetchType控制重新获取
    queryClient.invalidateQueries({
      queryKey: ['users'],
      refetchType: 'active', // 'active' | 'inactive' | 'all' | 'none'
    });
    
    // 使用predicate
    queryClient.invalidateQueries({
      predicate: (query) =>
        query.queryKey[0] === 'users' && query.queryKey[1]?.status === 'active',
    });
  };
  
  return <button onClick={handleInvalidate}>Invalidate Queries</button>;
}

条件失效

jsx
function ConditionalInvalidate() {
  const queryClient = useQueryClient();
  
  const { mutate } = useMutation({
    mutationFn: updateUser,
    
    onSuccess: (data, variables) => {
      // 只失效受影响的查询
      
      // 1. 失效单个用户
      queryClient.invalidateQueries({
        queryKey: ['user', variables.id],
      });
      
      // 2. 如果更新了状态,失效相关列表
      if (variables.status) {
        queryClient.invalidateQueries({
          queryKey: ['users', { status: variables.status }],
        });
      }
      
      // 3. 如果更新了团队,失效团队相关查询
      if (variables.teamId) {
        queryClient.invalidateQueries({
          queryKey: ['team', variables.teamId],
        });
        queryClient.invalidateQueries({
          queryKey: ['team', variables.teamId, 'members'],
        });
      }
    },
  });
  
  return <UpdateUserForm onSubmit={mutate} />;
}

重新获取

refetchQueries

jsx
function RefetchExample() {
  const queryClient = useQueryClient();
  
  const handleRefetch = async () => {
    // 重新获取所有查询
    await queryClient.refetchQueries();
    
    // 重新获取特定查询
    await queryClient.refetchQueries({ queryKey: ['users'] });
    
    // 只重新获取active查询
    await queryClient.refetchQueries({
      type: 'active',
    });
    
    // 使用predicate
    await queryClient.refetchQueries({
      predicate: (query) => query.queryKey[0] === 'users',
    });
    
    // 精确匹配
    await queryClient.refetchQueries({
      queryKey: ['users', { status: 'active' }],
      exact: true,
    });
  };
  
  return <button onClick={handleRefetch}>Refetch Queries</button>;
}

手动refetch

jsx
function ManualRefetch() {
  const { data, refetch, isFetching } = useQuery({
    queryKey: ['data'],
    queryFn: fetchData,
  });
  
  const handleRefresh = async () => {
    // 手动触发refetch
    const result = await refetch();
    
    if (result.isError) {
      console.error('Refetch failed:', result.error);
    } else {
      console.log('Refetch success:', result.data);
    }
  };
  
  return (
    <div>
      <button onClick={handleRefresh} disabled={isFetching}>
        {isFetching ? 'Refreshing...' : 'Refresh'}
      </button>
      <div>{JSON.stringify(data)}</div>
    </div>
  );
}

移除查询

removeQueries

jsx
function RemoveExample() {
  const queryClient = useQueryClient();
  
  const handleRemove = () => {
    // 移除所有查询
    queryClient.removeQueries();
    
    // 移除特定查询
    queryClient.removeQueries({ queryKey: ['user', 123] });
    
    // 移除匹配的查询
    queryClient.removeQueries({
      predicate: (query) => query.queryKey[0] === 'temp',
    });
  };
  
  return <button onClick={handleRemove}>Remove Queries</button>;
}

清除缓存

jsx
function ClearCache() {
  const queryClient = useQueryClient();
  
  const handleClear = () => {
    // 清除所有缓存
    queryClient.clear();
  };
  
  const handleLogout = () => {
    // 登出时清除用户相关缓存
    queryClient.removeQueries({
      predicate: (query) => {
        const key = query.queryKey[0];
        return ['user', 'profile', 'settings'].includes(key);
      },
    });
    
    // 或者清除所有并重定向
    queryClient.clear();
    window.location.href = '/login';
  };
  
  return (
    <div>
      <button onClick={handleClear}>Clear All Cache</button>
      <button onClick={handleLogout}>Logout</button>
    </div>
  );
}

预取数据

prefetchQuery

jsx
function PrefetchExample() {
  const queryClient = useQueryClient();
  const router = useRouter();
  
  const handleMouseEnter = async (userId) => {
    // 预取用户数据
    await queryClient.prefetchQuery({
      queryKey: ['user', userId],
      queryFn: () => fetchUser(userId),
      staleTime: 1000 * 60 * 5,
    });
  };
  
  return (
    <div>
      <Link
        to={`/users/${userId}`}
        onMouseEnter={() => handleMouseEnter(userId)}
      >
        View User
      </Link>
    </div>
  );
}

预取列表数据

jsx
function PrefetchList() {
  const queryClient = useQueryClient();
  
  const { data: users } = useQuery({
    queryKey: ['users'],
    queryFn: fetchUsers,
  });
  
  // 预取每个用户的详情
  useEffect(() => {
    users?.forEach(user => {
      queryClient.prefetchQuery({
        queryKey: ['user', user.id],
        queryFn: () => fetchUser(user.id),
      });
    });
  }, [users, queryClient]);
  
  return (
    <ul>
      {users?.map(user => (
        <li key={user.id}>
          <Link to={`/users/${user.id}`}>{user.name}</Link>
        </li>
      ))}
    </ul>
  );
}

路由预取

jsx
function RoutePrefe tch() {
  const queryClient = useQueryClient();
  const router = useRouter();
  
  const prefetchRoute = async (path) => {
    // 预取路由数据
    if (path === '/dashboard') {
      await Promise.all([
        queryClient.prefetchQuery({
          queryKey: ['stats'],
          queryFn: fetchStats,
        }),
        queryClient.prefetchQuery({
          queryKey: ['notifications'],
          queryFn: fetchNotifications,
        }),
      ]);
    }
    
    // 预取路由组件(Next.js)
    router.prefetch(path);
  };
  
  return (
    <nav>
      <Link
        to="/dashboard"
        onMouseEnter={() => prefetchRoute('/dashboard')}
      >
        Dashboard
      </Link>
    </nav>
  );
}

查询状态

获取查询状态

jsx
function QueryState() {
  const queryClient = useQueryClient();
  
  const handleCheckState = () => {
    const state = queryClient.getQueryState(['user', 123]);
    
    console.log({
      data: state?.data,
      dataUpdatedAt: state?.dataUpdatedAt,
      error: state?.error,
      errorUpdatedAt: state?.errorUpdatedAt,
      fetchFailureCount: state?.fetchFailureCount,
      fetchStatus: state?.fetchStatus,
      status: state?.status,
    });
  };
  
  return <button onClick={handleCheckState}>Check State</button>;
}

监听查询变化

jsx
function QuerySubscription() {
  const queryClient = useQueryClient();
  const [queryData, setQueryData] = useState(null);
  
  useEffect(() => {
    // 订阅查询变化
    const unsubscribe = queryClient.getQueryCache().subscribe((event) => {
      if (event.type === 'updated' && event.query.queryKey[0] === 'user') {
        setQueryData(event.query.state.data);
      }
    });
    
    return unsubscribe;
  }, [queryClient]);
  
  return <div>Latest user data: {JSON.stringify(queryData)}</div>;
}

缓存策略

分层缓存

jsx
// 全局默认配置
const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      staleTime: 1000 * 60,     // 1分钟
      cacheTime: 1000 * 60 * 5, // 5分钟
    },
  },
});

// 特定类型的配置
function UserQueries() {
  // 用户列表: 快速过期
  const { data: users } = useQuery({
    queryKey: ['users'],
    queryFn: fetchUsers,
    staleTime: 1000 * 30,        // 30秒
    cacheTime: 1000 * 60 * 2,    // 2分钟
  });
  
  // 用户详情: 较长缓存
  const { data: user } = useQuery({
    queryKey: ['user', userId],
    queryFn: () => fetchUser(userId),
    staleTime: 1000 * 60 * 5,    // 5分钟
    cacheTime: 1000 * 60 * 10,   // 10分钟
  });
  
  // 静态数据: 永久缓存
  const { data: config } = useQuery({
    queryKey: ['config'],
    queryFn: fetchConfig,
    staleTime: Infinity,
    cacheTime: Infinity,
  });
  
  return <div>...</div>;
}

智能缓存更新

jsx
function SmartCacheUpdate() {
  const queryClient = useQueryClient();
  
  const { mutate } = useMutation({
    mutationFn: updateUser,
    
    onSuccess: (updatedUser, variables) => {
      // 1. 直接更新单个用户缓存
      queryClient.setQueryData(['user', updatedUser.id], updatedUser);
      
      // 2. 更新用户列表
      queryClient.setQueryData(['users'], (old) =>
        old?.map(user =>
          user.id === updatedUser.id ? updatedUser : user
        )
      );
      
      // 3. 如果用户数据可能影响其他查询,失效它们
      if (variables.teamId !== updatedUser.teamId) {
        // 团队变更,失效旧团队和新团队
        queryClient.invalidateQueries({
          queryKey: ['team', variables.teamId],
        });
        queryClient.invalidateQueries({
          queryKey: ['team', updatedUser.teamId],
        });
      }
      
      // 4. 部分失效,保留某些查询
      queryClient.invalidateQueries({
        queryKey: ['users'],
        refetchType: 'none', // 不立即refetch
      });
    },
  });
  
  return <UpdateUserForm onSubmit={mutate} />;
}

缓存持久化

LocalStorage持久化

jsx
import { persistQueryClient } from '@tanstack/react-query-persist-client';
import { createSyncStoragePersister } from '@tanstack/query-sync-storage-persister';

const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      cacheTime: 1000 * 60 * 60 * 24, // 24小时
    },
  },
});

const persister = createSyncStoragePersister({
  storage: window.localStorage,
});

persistQueryClient({
  queryClient,
  persister,
  maxAge: 1000 * 60 * 60 * 24, // 24小时
});

IndexedDB持久化

jsx
import { createAsyncStoragePersister } from '@tanstack/query-async-storage-persister';
import { del, get, set } from 'idb-keyval';

const persister = createAsyncStoragePersister({
  storage: {
    getItem: get,
    setItem: set,
    removeItem: del,
  },
});

persistQueryClient({
  queryClient,
  persister,
  maxAge: 1000 * 60 * 60 * 24,
  buster: 'v1', // 版本号,改变时清除旧缓存
});

选择性持久化

jsx
const persister = createSyncStoragePersister({
  storage: window.localStorage,
  serialize: (data) => JSON.stringify(data),
  deserialize: (data) => JSON.parse(data),
});

persistQueryClient({
  queryClient,
  persister,
  
  // 只持久化特定查询
  dehydrateOptions: {
    shouldDehydrateQuery: (query) => {
      const queryKey = query.queryKey[0];
      
      // 不持久化临时查询
      if (queryKey === 'temp') return false;
      
      // 不持久化用户特定数据
      if (queryKey === 'user') return false;
      
      return true;
    },
  },
});

缓存调试

DevTools

jsx
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';

function App() {
  return (
    <QueryClientProvider client={queryClient}>
      <YourApp />
      
      <ReactQueryDevtools
        initialIsOpen={false}
        position="bottom-right"
        toggleButtonProps={{
          style: { marginLeft: '5px' },
        }}
      />
    </QueryClientProvider>
  );
}

缓存监控

jsx
function CacheMonitor() {
  const queryClient = useQueryClient();
  const [cacheStats, setCacheStats] = useState({});
  
  useEffect(() => {
    const updateStats = () => {
      const cache = queryClient.getQueryCache();
      const queries = cache.getAll();
      
      setCacheStats({
        total: queries.length,
        active: queries.filter(q => q.getObserversCount() > 0).length,
        inactive: queries.filter(q => q.getObserversCount() === 0).length,
        stale: queries.filter(q => q.isStale()).length,
        fetching: queries.filter(q => q.state.fetchStatus === 'fetching').length,
      });
    };
    
    const interval = setInterval(updateStats, 1000);
    updateStats();
    
    return () => clearInterval(interval);
  }, [queryClient]);
  
  return (
    <div className="cache-monitor">
      <h3>Cache Stats</h3>
      <ul>
        <li>Total Queries: {cacheStats.total}</li>
        <li>Active: {cacheStats.active}</li>
        <li>Inactive: {cacheStats.inactive}</li>
        <li>Stale: {cacheStats.stale}</li>
        <li>Fetching: {cacheStats.fetching}</li>
      </ul>
    </div>
  );
}

总结

查询缓存管理核心要点:

  1. QueryClient:获取、设置缓存数据
  2. 缓存失效:invalidateQueries条件失效
  3. 重新获取:refetchQueries按需刷新
  4. 移除查询:removeQueries、clear清理
  5. 预取数据:prefetchQuery提前加载
  6. 查询状态:getQueryState、订阅变化
  7. 缓存策略:分层缓存、智能更新
  8. 缓存持久化:LocalStorage、IndexedDB
  9. 缓存调试:DevTools、监控面板

合理的缓存管理能够显著提升应用性能和用户体验。

第四部分:高级缓存管理策略

4.1 缓存分层架构

jsx
// 1. 多级缓存系统
class MultiLevelCacheManager {
  constructor() {
    this.memoryCache = new Map(); // L1: 内存缓存
    this.sessionCache = sessionStorage; // L2: 会话缓存
    this.persistentCache = localStorage; // L3: 持久化缓存
  }

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

    // L2: 会话缓存
    const sessionData = this.sessionCache.getItem(key);
    if (sessionData) {
      const parsed = JSON.parse(sessionData);
      this.memoryCache.set(key, parsed);
      return parsed;
    }

    // L3: 持久化缓存
    const persistentData = this.persistentCache.getItem(key);
    if (persistentData) {
      const parsed = JSON.parse(persistentData);
      this.sessionCache.setItem(key, persistentData);
      this.memoryCache.set(key, parsed);
      return parsed;
    }

    return null;
  }

  set(key, value, options = {}) {
    const stringified = JSON.stringify(value);

    // L1: 始终写入内存
    this.memoryCache.set(key, value);

    // L2: 根据需要写入会话
    if (options.session !== false) {
      this.sessionCache.setItem(key, stringified);
    }

    // L3: 根据需要持久化
    if (options.persistent) {
      this.persistentCache.setItem(key, stringified);
    }
  }

  invalidate(key, level = 'all') {
    if (level === 'all' || level === 'memory') {
      this.memoryCache.delete(key);
    }
    if (level === 'all' || level === 'session') {
      this.sessionCache.removeItem(key);
    }
    if (level === 'all' || level === 'persistent') {
      this.persistentCache.removeItem(key);
    }
  }
}

const cacheManager = new MultiLevelCacheManager();

// 2. 自适应缓存策略
function useAdaptiveCaching(queryKey, fetcher) {
  const [cacheStrategy, setCacheStrategy] = useState('normal');
  const [networkSpeed, setNetworkSpeed] = useState('4g');

  useEffect(() => {
    // 检测网络速度
    const connection = navigator.connection || navigator.mozConnection || navigator.webkitConnection;
    if (connection) {
      setNetworkSpeed(connection.effectiveType);
    }

    const handleChange = () => {
      setNetworkSpeed(connection.effectiveType);
    };

    connection?.addEventListener('change', handleChange);
    return () => connection?.removeEventListener('change', handleChange);
  }, []);

  useEffect(() => {
    // 根据网络速度调整策略
    if (networkSpeed === 'slow-2g' || networkSpeed === '2g') {
      setCacheStrategy('aggressive'); // 激进缓存
    } else if (networkSpeed === '3g') {
      setCacheStrategy('normal');
    } else {
      setCacheStrategy('fresh'); // 更新鲜的数据
    }
  }, [networkSpeed]);

  const cacheConfig = {
    aggressive: { staleTime: 1000 * 60 * 30, gcTime: 1000 * 60 * 60 },
    normal: { staleTime: 1000 * 60 * 5, gcTime: 1000 * 60 * 30 },
    fresh: { staleTime: 1000 * 30, gcTime: 1000 * 60 * 5 }
  };

  return useQuery({
    queryKey,
    queryFn: fetcher,
    ...cacheConfig[cacheStrategy]
  });
}

// 3. 基于优先级的缓存清理
class PriorityCacheEviction {
  constructor(queryClient) {
    this.queryClient = queryClient;
    this.priorities = new Map();
  }

  setPriority(queryKey, priority) {
    this.priorities.set(JSON.stringify(queryKey), priority);
  }

  evictLowPriority(threshold = 50) {
    const cache = this.queryClient.getQueryCache();
    const queries = cache.getAll();

    const sortedQueries = queries
      .map(query => ({
        query,
        priority: this.priorities.get(JSON.stringify(query.queryKey)) || 0,
        size: this.estimateSize(query.state.data)
      }))
      .sort((a, b) => a.priority - b.priority);

    let evicted = 0;
    for (const { query, priority } of sortedQueries) {
      if (priority < threshold) {
        this.queryClient.removeQueries({ queryKey: query.queryKey });
        evicted++;
      }
    }

    return evicted;
  }

  estimateSize(data) {
    return JSON.stringify(data || {}).length;
  }
}

// 4. 智能预加载缓存
function useSmartPrefetch() {
  const queryClient = useQueryClient();
  const [viewport, setViewport] = useState({ top: 0, bottom: 0 });

  useEffect(() => {
    const handleScroll = () => {
      const top = window.scrollY;
      const bottom = top + window.innerHeight;
      setViewport({ top, bottom });
    };

    window.addEventListener('scroll', handleScroll);
    handleScroll();

    return () => window.removeEventListener('scroll', handleScroll);
  }, []);

  const prefetchIfVisible = useCallback((element, queryKey, queryFn) => {
    if (!element) return;

    const rect = element.getBoundingClientRect();
    const elementTop = rect.top + window.scrollY;
    const elementBottom = elementTop + rect.height;

    const PREFETCH_MARGIN = 200;
    const shouldPrefetch = 
      elementTop < viewport.bottom + PREFETCH_MARGIN &&
      elementBottom > viewport.top - PREFETCH_MARGIN;

    if (shouldPrefetch) {
      queryClient.prefetchQuery({ queryKey, queryFn });
    }
  }, [viewport, queryClient]);

  return prefetchIfVisible;
}

4.2 缓存同步与协调

jsx
// 1. 跨标签页缓存同步
function useCrossTabSync() {
  const queryClient = useQueryClient();

  useEffect(() => {
    const handleStorage = (e) => {
      if (e.key?.startsWith('query-update-')) {
        const queryKey = JSON.parse(e.key.replace('query-update-', ''));
        const newData = JSON.parse(e.newValue || 'null');

        if (newData) {
          queryClient.setQueryData(queryKey, newData);
        } else {
          queryClient.invalidateQueries({ queryKey });
        }
      }
    };

    window.addEventListener('storage', handleStorage);
    return () => window.removeEventListener('storage', handleStorage);
  }, [queryClient]);

  const broadcastUpdate = useCallback((queryKey, data) => {
    const key = `query-update-${JSON.stringify(queryKey)}`;
    localStorage.setItem(key, JSON.stringify(data));
    
    // 立即清除避免污染存储
    setTimeout(() => localStorage.removeItem(key), 100);
  }, []);

  return { broadcastUpdate };
}

// 2. 缓存版本控制
class CacheVersionManager {
  constructor() {
    this.version = this.loadVersion();
  }

  loadVersion() {
    return parseInt(localStorage.getItem('cache-version') || '0');
  }

  saveVersion(version) {
    localStorage.setItem('cache-version', version.toString());
    this.version = version;
  }

  checkVersion(queryClient) {
    const currentVersion = this.loadVersion();
    
    if (currentVersion < this.version) {
      // 版本不匹配,清空缓存
      queryClient.clear();
      this.saveVersion(this.version);
    }
  }

  incrementVersion(queryClient) {
    this.version++;
    this.saveVersion(this.version);
    queryClient.clear();
  }
}

const versionManager = new CacheVersionManager();

// 3. 缓存一致性检查
function useCacheConsistency(queryKey, options = {}) {
  const queryClient = useQueryClient();
  const { checkInterval = 60000, validator } = options;

  useEffect(() => {
    const checkConsistency = async () => {
      const cachedData = queryClient.getQueryData(queryKey);
      
      if (cachedData && validator) {
        const isValid = await validator(cachedData);
        
        if (!isValid) {
          console.warn('Cache inconsistency detected, invalidating:', queryKey);
          queryClient.invalidateQueries({ queryKey });
        }
      }
    };

    const interval = setInterval(checkConsistency, checkInterval);
    return () => clearInterval(interval);
  }, [queryKey, queryClient, checkInterval, validator]);
}

// 4. 缓存冲突解决
function useCacheConflictResolution() {
  const queryClient = useQueryClient();

  const resolveConflict = useCallback((queryKey, localData, serverData) => {
    // 时间戳比较
    if (localData.updatedAt && serverData.updatedAt) {
      return localData.updatedAt > serverData.updatedAt ? localData : serverData;
    }

    // 版本号比较
    if (localData.version !== undefined && serverData.version !== undefined) {
      return localData.version > serverData.version ? localData : serverData;
    }

    // 默认使用服务器数据
    return serverData;
  }, []);

  const mergeWithServer = useCallback(async (queryKey, fetchFn) => {
    const localData = queryClient.getQueryData(queryKey);
    const serverData = await fetchFn();

    if (!localData) return serverData;

    const resolved = resolveConflict(queryKey, localData, serverData);
    queryClient.setQueryData(queryKey, resolved);

    return resolved;
  }, [queryClient, resolveConflict]);

  return { mergeWithServer };
}

4.3 缓存性能优化

jsx
// 1. 缓存压缩
class CompressedCache {
  async compress(data) {
    const json = JSON.stringify(data);
    const blob = new Blob([json]);
    const stream = blob.stream();
    const compressedStream = stream.pipeThrough(
      new CompressionStream('gzip')
    );
    const compressedBlob = await new Response(compressedStream).blob();
    return await this.blobToBase64(compressedBlob);
  }

  async decompress(compressed) {
    const blob = await this.base64ToBlob(compressed);
    const stream = blob.stream();
    const decompressedStream = stream.pipeThrough(
      new DecompressionStream('gzip')
    );
    const decompressedBlob = await new Response(decompressedStream).blob();
    const text = await decompressedBlob.text();
    return JSON.parse(text);
  }

  blobToBase64(blob) {
    return new Promise((resolve) => {
      const reader = new FileReader();
      reader.onloadend = () => resolve(reader.result);
      reader.readAsDataURL(blob);
    });
  }

  async base64ToBlob(base64) {
    const res = await fetch(base64);
    return res.blob();
  }
}

// 2. 缓存分片
class ChunkedCache {
  constructor(chunkSize = 100000) {
    this.chunkSize = chunkSize;
  }

  chunk(key, data) {
    const json = JSON.stringify(data);
    const chunks = [];
    
    for (let i = 0; i < json.length; i += this.chunkSize) {
      chunks.push(json.slice(i, i + this.chunkSize));
    }

    const metadata = {
      key,
      totalChunks: chunks.length,
      timestamp: Date.now()
    };

    localStorage.setItem(`${key}-meta`, JSON.stringify(metadata));
    
    chunks.forEach((chunk, index) => {
      localStorage.setItem(`${key}-chunk-${index}`, chunk);
    });
  }

  reassemble(key) {
    const metaData = localStorage.getItem(`${key}-meta`);
    if (!metaData) return null;

    const meta = JSON.parse(metaData);
    let json = '';

    for (let i = 0; i < meta.totalChunks; i++) {
      const chunk = localStorage.getItem(`${key}-chunk-${i}`);
      if (!chunk) return null;
      json += chunk;
    }

    return JSON.parse(json);
  }

  clear(key) {
    const metaData = localStorage.getItem(`${key}-meta`);
    if (!metaData) return;

    const meta = JSON.parse(metaData);
    
    for (let i = 0; i < meta.totalChunks; i++) {
      localStorage.removeItem(`${key}-chunk-${i}`);
    }
    
    localStorage.removeItem(`${key}-meta`);
  }
}

// 3. 缓存预热
class CacheWarmer {
  constructor(queryClient) {
    this.queryClient = queryClient;
    this.warmupQueue = [];
  }

  schedule(queryKey, queryFn, priority = 0) {
    this.warmupQueue.push({ queryKey, queryFn, priority });
    this.warmupQueue.sort((a, b) => b.priority - a.priority);
  }

  async warmup(concurrency = 3) {
    const batches = [];
    for (let i = 0; i < this.warmupQueue.length; i += concurrency) {
      batches.push(this.warmupQueue.slice(i, i + concurrency));
    }

    for (const batch of batches) {
      await Promise.all(
        batch.map(({ queryKey, queryFn }) =>
          this.queryClient.prefetchQuery({ queryKey, queryFn })
        )
      );
    }

    this.warmupQueue = [];
  }

  async warmupCritical() {
    const critical = this.warmupQueue.filter(item => item.priority >= 8);
    
    await Promise.all(
      critical.map(({ queryKey, queryFn }) =>
        this.queryClient.prefetchQuery({ queryKey, queryFn })
      )
    );
  }
}

// 4. 缓存内存监控
class CacheMemoryMonitor {
  constructor(queryClient, maxSize = 50 * 1024 * 1024) {
    this.queryClient = queryClient;
    this.maxSize = maxSize;
  }

  estimateSize() {
    const cache = this.queryClient.getQueryCache();
    const queries = cache.getAll();
    
    let totalSize = 0;
    const sizes = [];

    queries.forEach(query => {
      const size = new Blob([JSON.stringify(query.state.data)]).size;
      totalSize += size;
      sizes.push({
        queryKey: query.queryKey,
        size,
        lastAccessed: query.state.dataUpdatedAt
      });
    });

    return { totalSize, sizes, count: queries.length };
  }

  enforce() {
    const { totalSize, sizes } = this.estimateSize();

    if (totalSize > this.maxSize) {
      // 按最后访问时间排序,删除最旧的
      const sorted = sizes.sort((a, b) => a.lastAccessed - b.lastAccessed);
      
      let freed = 0;
      for (const item of sorted) {
        if (totalSize - freed < this.maxSize * 0.8) break;
        
        this.queryClient.removeQueries({ queryKey: item.queryKey });
        freed += item.size;
      }

      return freed;
    }

    return 0;
  }

  monitor(interval = 30000) {
    return setInterval(() => {
      const { totalSize, count } = this.estimateSize();
      
      console.log(`Cache: ${count} queries, ${(totalSize / 1024 / 1024).toFixed(2)} MB`);
      
      if (totalSize > this.maxSize) {
        const freed = this.enforce();
        console.log(`Freed ${(freed / 1024 / 1024).toFixed(2)} MB`);
      }
    }, interval);
  }
}

4.4 缓存调试与监控

jsx
// 1. 缓存调试面板
function CacheDebugPanel() {
  const queryClient = useQueryClient();
  const [queries, setQueries] = useState([]);
  const [stats, setStats] = useState({});

  useEffect(() => {
    const updateStats = () => {
      const cache = queryClient.getQueryCache();
      const allQueries = cache.getAll();

      const queryList = allQueries.map(query => ({
        key: JSON.stringify(query.queryKey),
        status: query.state.status,
        dataUpdatedAt: query.state.dataUpdatedAt,
        errorUpdatedAt: query.state.errorUpdatedAt,
        isFetching: query.state.fetchStatus === 'fetching',
        observers: query.getObserversCount(),
        size: new Blob([JSON.stringify(query.state.data)]).size
      }));

      const totalSize = queryList.reduce((sum, q) => sum + q.size, 0);
      const avgSize = totalSize / queryList.length || 0;

      setQueries(queryList);
      setStats({
        total: queryList.length,
        totalSize,
        avgSize,
        fetching: queryList.filter(q => q.isFetching).length,
        stale: queryList.filter(q => query.state.isStale).length
      });
    };

    const interval = setInterval(updateStats, 1000);
    updateStats();

    return () => clearInterval(interval);
  }, [queryClient]);

  return (
    <div style={{ padding: 20, fontFamily: 'monospace' }}>
      <h2>缓存调试面板</h2>
      
      <div style={{ marginBottom: 20 }}>
        <h3>统计信息</h3>
        <p>总查询数: {stats.total}</p>
        <p>总大小: {(stats.totalSize / 1024).toFixed(2)} KB</p>
        <p>平均大小: {(stats.avgSize / 1024).toFixed(2)} KB</p>
        <p>加载中: {stats.fetching}</p>
        <p>过期: {stats.stale}</p>
      </div>

      <div>
        <h3>查询列表</h3>
        <table style={{ width: '100%', borderCollapse: 'collapse' }}>
          <thead>
            <tr>
              <th>查询键</th>
              <th>状态</th>
              <th>观察者</th>
              <th>大小</th>
              <th>最后更新</th>
            </tr>
          </thead>
          <tbody>
            {queries.map((query, index) => (
              <tr key={index}>
                <td>{query.key}</td>
                <td>{query.status}</td>
                <td>{query.observers}</td>
                <td>{(query.size / 1024).toFixed(2)} KB</td>
                <td>{new Date(query.dataUpdatedAt).toLocaleTimeString()}</td>
              </tr>
            ))}
          </tbody>
        </table>
      </div>
    </div>
  );
}

// 2. 缓存性能分析
class CachePerformanceAnalyzer {
  constructor() {
    this.metrics = [];
  }

  recordHit(queryKey, source) {
    this.metrics.push({
      type: 'hit',
      queryKey: JSON.stringify(queryKey),
      source, // 'memory', 'stale', 'fresh'
      timestamp: Date.now()
    });
  }

  recordMiss(queryKey) {
    this.metrics.push({
      type: 'miss',
      queryKey: JSON.stringify(queryKey),
      timestamp: Date.now()
    });
  }

  getHitRate(queryKey) {
    const key = JSON.stringify(queryKey);
    const hits = this.metrics.filter(m => m.queryKey === key && m.type === 'hit').length;
    const misses = this.metrics.filter(m => m.queryKey === key && m.type === 'miss').length;
    const total = hits + misses;
    
    return total > 0 ? (hits / total) * 100 : 0;
  }

  getReport() {
    const keys = [...new Set(this.metrics.map(m => m.queryKey))];
    
    return keys.map(key => ({
      queryKey: key,
      hitRate: this.getHitRate(JSON.parse(key)),
      totalRequests: this.metrics.filter(m => m.queryKey === key).length,
      lastAccess: Math.max(...this.metrics
        .filter(m => m.queryKey === key)
        .map(m => m.timestamp))
    }));
  }
}

缓存管理最佳实践总结

1. 分层架构
   ✅ 多级缓存(内存/会话/持久化)
   ✅ 自适应策略
   ✅ 优先级驱动清理

2. 同步协调
   ✅ 跨标签页同步
   ✅ 版本控制
   ✅ 一致性检查
   ✅ 冲突解决

3. 性能优化
   ✅ 缓存压缩
   ✅ 数据分片
   ✅ 预热策略
   ✅ 内存监控

4. 调试监控
   ✅ 可视化面板
   ✅ 性能分析
   ✅ 命中率追踪
   ✅ 实时统计

高效的缓存管理是构建高性能React应用的关键,合理运用这些策略能显著提升用户体验。