From ae14b33f832e4de8c06f9c7139214c79f18e8396 Mon Sep 17 00:00:00 2001 From: TerryM Date: Sat, 30 May 2026 02:04:26 +0800 Subject: [PATCH] =?UTF-8?q?perf:=20=E8=B5=84=E6=96=99=E6=B5=81=E4=BC=9A?= =?UTF-8?q?=E8=AF=9D=E7=BA=A7=E7=BC=93=E5=AD=98,=E5=88=87=E6=8D=A2?= =?UTF-8?q?=E9=A1=B5=E9=9D=A2=E4=B8=8D=E5=86=8D=E9=87=8D=E8=BD=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit usePostStream 增加按筛选参数为 key 的内存缓存,保存已加载的全部内容与分页 游标。再次进入同一视图(如首页⇄全部资料来回切)时直接还原,不重置、不重新 请求、不显示骨架屏。整页刷新清空缓存以获取最新数据。 Co-Authored-By: Claude Opus 4.8 (1M context) --- .../messageStream/hooks/usePostStream.ts | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/src/components/messageStream/hooks/usePostStream.ts b/src/components/messageStream/hooks/usePostStream.ts index fc7643b..907a1b5 100644 --- a/src/components/messageStream/hooks/usePostStream.ts +++ b/src/components/messageStream/hooks/usePostStream.ts @@ -94,6 +94,24 @@ function buildRealUrl(params: PostStreamParams, cursor?: string): string { return `${q ? "/api/posts/search" : "/api/posts"}?${sp.toString()}`; } +type CachedStream = { + items: Post[]; + cursor: string | undefined; + hasMore: boolean; +}; + +/** + * Session cache of loaded stream state, keyed by the request params. Lets a + * view that's been seen before (e.g. switching Home ⇄ All) restore instantly — + * all loaded pages, no skeleton, no refetch — instead of reloading from page 1. + * In-memory only: a full page reload starts fresh. + */ +const streamCache = new Map(); + +function streamKey(params: PostStreamParams): string { + return buildRealUrl(params); +} + export function usePostStream(params: PostStreamParams): PostStreamResult { const [items, setItems] = useState([]); const [hasMore, setHasMore] = useState(true); @@ -172,6 +190,17 @@ export function usePostStream(params: PostStreamParams): PostStreamResult { ); useEffect(() => { + // Restore a previously-loaded view instantly (no reset, no refetch). + const cached = streamCache.get(streamKey(params)); + if (cached) { + setItems(cached.items); + cursorRef.current = cached.cursor; + setHasMore(cached.hasMore); + hasMoreRef.current = cached.hasMore; + setIsLoading(false); + setError(null); + return; + } setItems([]); cursorRef.current = undefined; setHasMore(true); @@ -188,6 +217,17 @@ export function usePostStream(params: PostStreamParams): PostStreamResult { params.lang, ]); + // Persist loaded state so returning to this view restores it from cache. + useEffect(() => { + if (items.length === 0) return; + streamCache.set(streamKey(params), { + items, + cursor: cursorRef.current, + hasMore, + }); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [items, hasMore]); + const loadMore = useCallback(() => { fetchPage(false); }, [fetchPage]);