diff --git a/src/app/page.tsx b/src/app/page.tsx index bcc4aa9..2c7e276 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -575,7 +575,7 @@ function HomeClient() { {/* TMDB 热门轮播图 */} {homeBannerEnabled && (
- +
)} diff --git a/src/components/BannerCarousel.tsx b/src/components/BannerCarousel.tsx index 91c3a4e..c807de4 100644 --- a/src/components/BannerCarousel.tsx +++ b/src/components/BannerCarousel.tsx @@ -10,6 +10,7 @@ import { processImageUrl } from '@/lib/utils'; interface BannerCarouselProps { autoPlayInterval?: number; // 自动播放间隔(毫秒) + delayLoad?: boolean; // 是否延迟加载(等页面加载完毕后再加载) } // 扩展TMDBItem类型以支持TX数据源的额外字段 @@ -20,11 +21,12 @@ interface BannerItem extends TMDBItem { genres?: string[]; // 豆瓣数据源的类型标签 } -export default function BannerCarousel({ autoPlayInterval = 5000 }: BannerCarouselProps) { +export default function BannerCarousel({ autoPlayInterval = 5000, delayLoad = false }: BannerCarouselProps) { const router = useRouter(); const [items, setItems] = useState([]); const [currentIndex, setCurrentIndex] = useState(0); const [isLoading, setIsLoading] = useState(true); + const [shouldLoad, setShouldLoad] = useState(!delayLoad); // 是否应该开始加载数据 const [isPaused, setIsPaused] = useState(false); const [skipNextAutoPlay, setSkipNextAutoPlay] = useState(false); // 跳过下一次自动播放 const [isYouTubeAccessible, setIsYouTubeAccessible] = useState(false); // YouTube连通性(默认false,检查后再决定) @@ -76,6 +78,22 @@ export default function BannerCarousel({ autoPlayInterval = 5000 }: BannerCarous } }, []); + // 延迟加载:等待页面加载完毕后再开始加载轮播图数据 + useEffect(() => { + if (!delayLoad) return; + + // 页面加载完毕后再开始加载 + if (document.readyState === 'complete') { + setShouldLoad(true); + } else { + const handleLoad = () => { + setShouldLoad(true); + }; + window.addEventListener('load', handleLoad); + return () => window.removeEventListener('load', handleLoad); + } + }, [delayLoad]); + // 检测YouTube连通性 - 仅在启用预告片且数据源为TMDB时检测 useEffect(() => { // 如果未启用预告片或数据源不是TMDB,不进行检测 @@ -110,13 +128,19 @@ export default function BannerCarousel({ autoPlayInterval = 5000 }: BannerCarous // 获取热门内容 useEffect(() => { + // 如果不应该加载,直接返回 + if (!shouldLoad) return; + const fetchTrending = async () => { try { - // 先尝试从所有可能的数据源缓存中读取 + // 先尝试从所有可能的数据源缓存中读取,找到最新的缓存 const sources = ['TMDB', 'TX', 'Douban']; let cachedData = null; let validSource = null; + let cacheExpired = false; + let latestTimestamp = 0; + // 遍历所有数据源,找到最新的缓存 for (const source of sources) { const cacheKey = getLocalStorageKey(source); const cached = localStorage.getItem(cacheKey); @@ -124,13 +148,13 @@ export default function BannerCarousel({ autoPlayInterval = 5000 }: BannerCarous if (cached) { try { const { data, timestamp } = JSON.parse(cached); - const now = Date.now(); - // 如果缓存未过期,使用缓存数据 - if (now - timestamp < LOCALSTORAGE_DURATION) { + // 选择时间戳最新的缓存 + if (timestamp > latestTimestamp) { cachedData = data; validSource = source; - break; + latestTimestamp = timestamp; + cacheExpired = Date.now() - timestamp > LOCALSTORAGE_DURATION; } } catch (e) { console.error('解析缓存数据失败:', e); @@ -138,34 +162,35 @@ export default function BannerCarousel({ autoPlayInterval = 5000 }: BannerCarous } } - // 如果有有效的缓存,直接使用,不请求API + // 乐观缓存:如果有缓存(无论是否过期),先显示缓存数据 if (cachedData) { setItems(cachedData); setDataSource(validSource || ''); // 设置数据源 setIsLoading(false); - return; } - // 没有缓存或缓存过期,从 API 获取数据 - const response = await fetch('/api/tmdb/trending'); - const result = await response.json(); + // 如果缓存过期或没有缓存,后台更新数据 + if (!cachedData || cacheExpired) { + const response = await fetch('/api/tmdb/trending'); + const result = await response.json(); - if (result.code === 200 && result.list.length > 0) { - const dataSource = result.source || 'TMDB'; // 获取数据源标识 - const cacheKey = getLocalStorageKey(dataSource); + if (result.code === 200 && result.list.length > 0) { + const newDataSource = result.source || 'TMDB'; // 获取数据源标识 + const cacheKey = getLocalStorageKey(newDataSource); - setItems(result.list); - setDataSource(dataSource); // 设置数据源 + setItems(result.list); + setDataSource(newDataSource); // 设置数据源 - // 保存到 localStorage(使用数据源特定的key) - try { - localStorage.setItem(cacheKey, JSON.stringify({ - data: result.list, - timestamp: Date.now() - })); - } catch (e) { - // localStorage 可能已满,忽略错误 - console.error('保存到 localStorage 失败:', e); + // 保存到 localStorage(使用数据源特定的key) + try { + localStorage.setItem(cacheKey, JSON.stringify({ + data: result.list, + timestamp: Date.now() + })); + } catch (e) { + // localStorage 可能已满,忽略错误 + console.error('保存到 localStorage 失败:', e); + } } } } catch (error) { @@ -176,7 +201,7 @@ export default function BannerCarousel({ autoPlayInterval = 5000 }: BannerCarous }; fetchTrending(); - }, []); + }, [shouldLoad]); // 自动播放 useEffect(() => { @@ -262,12 +287,17 @@ export default function BannerCarousel({ autoPlayInterval = 5000 }: BannerCarous touchEndX.current = 0; }; - if (isLoading) { + if (isLoading || !shouldLoad) { return ( -
-
-
-
+
+ MoonTVPlus
); }