perf: loadWatchlist 添加 localStorage 缓存(7天有效期)

- 添加 loadWatchlistFromCache/saveWatchlistToCache 缓存工具函数
- loadWatchlist 三级缓存策略:Redux → localStorage → API
- toggleWatchlist 成功后自动同步更新缓存
- 减少重复 API 请求,提升页面加载性能

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
zdl
2025-12-10 11:01:33 +08:00
parent 8786fa7b06
commit 7c83ffe008

View File

@@ -4,6 +4,56 @@ import { eventService, stockService } from '../../services/eventService';
import { logger } from '../../utils/logger'; import { logger } from '../../utils/logger';
import { getApiBase } from '../../utils/apiConfig'; import { getApiBase } from '../../utils/apiConfig';
// ==================== Watchlist 缓存配置 ====================
const WATCHLIST_CACHE_KEY = 'watchlist_cache';
const WATCHLIST_CACHE_DURATION = 7 * 24 * 60 * 60 * 1000; // 7天
/**
* 从 localStorage 读取自选股缓存
*/
const loadWatchlistFromCache = () => {
try {
const cached = localStorage.getItem(WATCHLIST_CACHE_KEY);
if (!cached) return null;
const { data, timestamp } = JSON.parse(cached);
const now = Date.now();
// 检查缓存是否过期7天
if (now - timestamp > WATCHLIST_CACHE_DURATION) {
localStorage.removeItem(WATCHLIST_CACHE_KEY);
logger.debug('stockSlice', '自选股缓存已过期');
return null;
}
logger.debug('stockSlice', '自选股 localStorage 缓存命中', {
count: data?.length || 0,
age: Math.round((now - timestamp) / 1000 / 60) + '分钟前'
});
return data;
} catch (error) {
logger.error('stockSlice', 'loadWatchlistFromCache', error);
return null;
}
};
/**
* 保存自选股到 localStorage
*/
const saveWatchlistToCache = (data) => {
try {
localStorage.setItem(WATCHLIST_CACHE_KEY, JSON.stringify({
data,
timestamp: Date.now()
}));
logger.debug('stockSlice', '自选股已缓存到 localStorage', {
count: data?.length || 0
});
} catch (error) {
logger.error('stockSlice', 'saveWatchlistToCache', error);
}
};
// ==================== Async Thunks ==================== // ==================== Async Thunks ====================
/** /**
@@ -153,13 +203,28 @@ export const fetchExpectationScore = createAsyncThunk(
/** /**
* 加载用户自选股列表(包含完整信息) * 加载用户自选股列表(包含完整信息)
* 缓存策略Redux 内存缓存 → localStorage 持久缓存7天 → API 请求
*/ */
export const loadWatchlist = createAsyncThunk( export const loadWatchlist = createAsyncThunk(
'stock/loadWatchlist', 'stock/loadWatchlist',
async () => { async (_, { getState }) => {
logger.debug('stockSlice', 'loadWatchlist'); logger.debug('stockSlice', 'loadWatchlist');
try { try {
// 1. 先检查 Redux 内存缓存
const reduxCached = getState().stock.watchlist;
if (reduxCached && reduxCached.length > 0) {
logger.debug('stockSlice', 'Redux watchlist 缓存命中', { count: reduxCached.length });
return reduxCached;
}
// 2. 再检查 localStorage 持久缓存7天有效期
const localCached = loadWatchlistFromCache();
if (localCached && localCached.length > 0) {
return localCached;
}
// 3. 缓存无效,调用 API
const apiBase = getApiBase(); const apiBase = getApiBase();
const response = await fetch(`${apiBase}/api/account/watchlist`, { const response = await fetch(`${apiBase}/api/account/watchlist`, {
credentials: 'include' credentials: 'include'
@@ -172,6 +237,10 @@ export const loadWatchlist = createAsyncThunk(
stock_code: item.stock_code, stock_code: item.stock_code,
stock_name: item.stock_name, stock_name: item.stock_name,
})); }));
// 保存到 localStorage 缓存
saveWatchlistToCache(watchlistData);
logger.debug('stockSlice', '自选股列表加载成功', { logger.debug('stockSlice', '自选股列表加载成功', {
count: watchlistData.length count: watchlistData.length
}); });
@@ -490,9 +559,10 @@ const stockSlice = createSlice({
state.watchlist = state.watchlist.filter(item => item.stock_code !== stockCode); state.watchlist = state.watchlist.filter(item => item.stock_code !== stockCode);
} }
}) })
// fulfilled: 乐观更新模式下状态已在 pending 更新,这里无需操作 // fulfilled: 同步更新 localStorage 缓存
.addCase(toggleWatchlist.fulfilled, () => { .addCase(toggleWatchlist.fulfilled, (state) => {
// 状态已在 pending 时更新 // 状态已在 pending 时更新,这里同步到 localStorage
saveWatchlistToCache(state.watchlist);
}); });
} }
}); });