diff --git a/apps/ai-game-creator-shell/src/view/project-development/chat/components/DirectProjectConversation/conversationScrollAnchor.ts b/apps/ai-game-creator-shell/src/view/project-development/chat/components/DirectProjectConversation/conversationScrollAnchor.ts index 765104d5a..a16c65926 100644 --- a/apps/ai-game-creator-shell/src/view/project-development/chat/components/DirectProjectConversation/conversationScrollAnchor.ts +++ b/apps/ai-game-creator-shell/src/view/project-development/chat/components/DirectProjectConversation/conversationScrollAnchor.ts @@ -84,6 +84,14 @@ export function readTopVisibleTurnAnchor( ): ConversationTurnAnchor | null { const listTop = list.getBoundingClientRect().top; + // TODO(perf): 这个循环最坏是 O(锚点之前的块数) 次 `getBoundingClientRect()`,调用点是 `onScroll` + // (离底时)与每次 `compensateLayout`,超长历史里属于 low 级别的量测开销——同一帧里第一次调用 + // 之后布局已经干净,后续调用不会各自再触发一次 reflow,所以不是「每帧几百次强制布局」的振荡源。 + // 真要省掉遍历只能按「块在文档顺序里位置单调递增」做二分、或从上次命中处续扫;但被折进**收起** + // `
` 的块矩形全 0、同样在候选集合里,会打断这个单调性,二分前得先定「隐藏块怎么参与」, + // 而给锚点集合加「只收可见块」的规则又要每次子元素变化先量一遍,等于把省下的遍历又搬回来。 + // 若真机在超长历史里量到滚动手感问题,先走「缓存上次命中的块身份 + 从它附近续扫」这条不破坏 + // 单调性假设的路,而不是 rAF 节流(滚动事件本就按帧派发,同一帧去重收益极小)。 for (const block of collectTurnBlocks(list)) { const rect = block.getBoundingClientRect(); // 折进收起 `
` 的块在真实浏览器里没有布局盒(矩形全 0):它既不是用户正在读的