Appearance
查询缓存管理
概述
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>
);
}总结
查询缓存管理核心要点:
- QueryClient:获取、设置缓存数据
- 缓存失效:invalidateQueries条件失效
- 重新获取:refetchQueries按需刷新
- 移除查询:removeQueries、clear清理
- 预取数据:prefetchQuery提前加载
- 查询状态:getQueryState、订阅变化
- 缓存策略:分层缓存、智能更新
- 缓存持久化:LocalStorage、IndexedDB
- 缓存调试: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应用的关键,合理运用这些策略能显著提升用户体验。