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:
@@ -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);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user