DOM 操作优化与缓存管理
性能优化 - React.memo、API并行化、useReducer重构
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
// HeroPanel - 综合日历组件
|
||||
import React, { useState, useEffect, useCallback, Suspense, lazy, memo } from "react";
|
||||
import React, { useState, useEffect, useCallback, Suspense, lazy, memo, useRef } from "react";
|
||||
import {
|
||||
Box,
|
||||
HStack,
|
||||
@@ -43,12 +43,23 @@ const CombinedCalendar = memo(({ DetailModal }) => {
|
||||
const [detailLoading, setDetailLoading] = useState(false);
|
||||
const [modalOpen, setModalOpen] = useState(false);
|
||||
|
||||
// 加载日历综合数据(一次 API 调用获取所有数据)
|
||||
// 月份数据缓存(避免切换月份后再切回时重复请求)
|
||||
const monthCacheRef = useRef({});
|
||||
|
||||
// 加载日历综合数据(带缓存)
|
||||
useEffect(() => {
|
||||
const loadCalendarCombinedData = async () => {
|
||||
const year = currentMonth.getFullYear();
|
||||
const month = currentMonth.getMonth() + 1;
|
||||
const cacheKey = `${year}-${month}`;
|
||||
|
||||
// 检查缓存
|
||||
if (monthCacheRef.current[cacheKey]) {
|
||||
setCalendarData(monthCacheRef.current[cacheKey]);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const year = currentMonth.getFullYear();
|
||||
const month = currentMonth.getMonth() + 1;
|
||||
const response = await fetch(
|
||||
`${getApiBase()}/api/v1/calendar/combined-data?year=${year}&month=${month}`
|
||||
);
|
||||
@@ -63,10 +74,8 @@ const CombinedCalendar = memo(({ DetailModal }) => {
|
||||
eventCount: item.event_count || 0,
|
||||
indexChange: item.index_change,
|
||||
}));
|
||||
console.log(
|
||||
"[HeroPanel] 加载日历综合数据成功,数据条数:",
|
||||
formattedData.length
|
||||
);
|
||||
// 存入缓存
|
||||
monthCacheRef.current[cacheKey] = formattedData;
|
||||
setCalendarData(formattedData);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -268,14 +268,23 @@ const DetailModal = ({
|
||||
[dispatch, isStockInWatchlist]
|
||||
);
|
||||
|
||||
// 加载股票行情(并行加载优化)
|
||||
// 加载股票行情(并行加载 + 缓存去重)
|
||||
const loadStockQuotes = useCallback(
|
||||
async (stocks) => {
|
||||
if (!stocks || stocks.length === 0) return;
|
||||
|
||||
// 过滤已缓存的股票,只请求未缓存的
|
||||
const uncachedStocks = stocks.filter(
|
||||
(stock) => !stockQuotes[stock.code]
|
||||
);
|
||||
|
||||
// 如果全部已缓存,无需请求
|
||||
if (uncachedStocks.length === 0) return;
|
||||
|
||||
setStockQuotesLoading(true);
|
||||
|
||||
// 并行发起所有请求
|
||||
const promises = stocks.map(async (stock) => {
|
||||
// 并行发起未缓存股票的请求
|
||||
const promises = uncachedStocks.map(async (stock) => {
|
||||
const code = getSixDigitCode(stock.code);
|
||||
try {
|
||||
const response = await fetch(
|
||||
@@ -304,18 +313,18 @@ const DetailModal = ({
|
||||
// 等待所有请求完成
|
||||
const results = await Promise.all(promises);
|
||||
|
||||
// 构建 quotes 对象
|
||||
const quotes = {};
|
||||
// 合并新数据到现有缓存
|
||||
const newQuotes = { ...stockQuotes };
|
||||
results.forEach((result) => {
|
||||
if (result) {
|
||||
quotes[result.stockCode] = result.quote;
|
||||
newQuotes[result.stockCode] = result.quote;
|
||||
}
|
||||
});
|
||||
|
||||
setStockQuotes(quotes);
|
||||
setStockQuotes(newQuotes);
|
||||
setStockQuotesLoading(false);
|
||||
},
|
||||
[setStockQuotes, setStockQuotesLoading]
|
||||
[stockQuotes, setStockQuotes, setStockQuotesLoading]
|
||||
);
|
||||
|
||||
// 显示相关股票
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* Y轴:板块热度(涨停家数)
|
||||
* 支持时间滑动条查看历史数据
|
||||
*/
|
||||
import React, { useState, useEffect, useMemo, useCallback } from "react";
|
||||
import React, { useState, useEffect, useMemo, useCallback, useRef } from "react";
|
||||
import {
|
||||
Box,
|
||||
Text,
|
||||
@@ -38,6 +38,9 @@ import {
|
||||
/**
|
||||
* ThemeCometChart 主组件
|
||||
*/
|
||||
// 缓存有效期(5 分钟)
|
||||
const CACHE_DURATION = 5 * 60 * 1000;
|
||||
|
||||
const ThemeCometChart = ({ onThemeSelect }) => {
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [allDatesData, setAllDatesData] = useState({});
|
||||
@@ -48,8 +51,24 @@ const ThemeCometChart = ({ onThemeSelect }) => {
|
||||
const { isOpen, onOpen, onClose } = useDisclosure();
|
||||
const toast = useToast();
|
||||
|
||||
// 加载所有日期的数据
|
||||
// 数据缓存(避免 tab 切换时重复请求)
|
||||
const dataCacheRef = useRef({ data: null, dates: null, timestamp: null });
|
||||
|
||||
// 加载所有日期的数据(带缓存)
|
||||
const loadAllData = useCallback(async () => {
|
||||
// 检查缓存是否有效(5分钟内)
|
||||
const now = Date.now();
|
||||
if (
|
||||
dataCacheRef.current.timestamp &&
|
||||
now - dataCacheRef.current.timestamp < CACHE_DURATION &&
|
||||
dataCacheRef.current.data
|
||||
) {
|
||||
setAllDatesData(dataCacheRef.current.data);
|
||||
setAvailableDates(dataCacheRef.current.dates);
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
|
||||
setLoading(true);
|
||||
try {
|
||||
const apiBase = getApiBase();
|
||||
@@ -109,6 +128,12 @@ const ThemeCometChart = ({ onThemeSelect }) => {
|
||||
}
|
||||
});
|
||||
|
||||
// 存入缓存
|
||||
dataCacheRef.current = {
|
||||
data: dataCache,
|
||||
dates: dates,
|
||||
timestamp: Date.now(),
|
||||
};
|
||||
setAllDatesData(dataCache);
|
||||
setSliderIndex(0);
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user