
浏览器弹出「页面无响应」或长时间转圈本质是主线程被同步任务占满超过几秒。在这个项目里大模型请求期间有几条路径会叠加阻塞下面按影响和改法说明。卡死从哪里来1. 流式更新图表最大瓶颈AI 生成display_diagram时XML 会不断流式推送。chat-message-display.tsx里大约每150ms触发一次图表更新const STREAMING_DEBOUNCE_MS 150 // Keep original 150ms debounce timing每次handleDisplayChart会在主线程同步执行DOMParser解析 XML多次replaceNodes合并节点又一次 DOM 解析/序列化validateAndFixXml→validateMxCellStructureDOM 大量正则loadDiagram→drawioRef.load()重载 iframe 内容const handleDisplayChart useCallback( (xml: string, showToast false) { // ... DOMParser → replaceNodes → validateAndFixXml → onDisplayChartsetChartXML(xmlToLoad) if (drawioRef.current) { drawioRef.current.load({ xml: xmlToLoad, })Draw.io iframe 的load()非常重流式阶段若每秒多次调用很容易触发浏览器「页面无响应」弹框。2. 每条流式 token 触发整页 React 重渲染useChat每次收到数据都会更新messages导致ChatMessageDisplay整棵消息树messages.map重渲染useEffect([messages, handleDisplayChart])再次执行ReactMarkdown反复解析正在增长的文本}, [messages, handleDisplayChart])3. localStorage 同步写入对话和 XML 会 debounce 1 秒后JSON.stringify写入 localStorage流式期间若消息/XML 很大写入会短暂阻塞主线程useEffect(() { // ... localStorageDebounceRef.current setTimeout(() { localStorage.setItem( STORAGE_MESSAGES_KEY, JSON.stringify(messages), ) }, LOCAL_STORAGE_DEBOUNCE_MS)4. 开发模式下的日志开销validateMxCellStructure在热路径上有大量console.time/console.log开发模式下会明显拖慢console.time(perf:validateMxCellStructure) console.log(perf:validateMxCellStructure XML size: ${xml.length} bytes)优化方向按收益排序方案 A流式阶段不实时刷新 Draw.io收益最大改动相对小思路input-streaming/input-available时只显示「生成中」占位仅在output-available最终 XML时调用一次loadDiagram。当前逻辑在流式阶段就会 debounce 更新图表约 520–551 行。可改为流式只更新聊天区里的预览文本/进度条完成再执行一次完整的校验 loadDiagram这样通常能消除大部分假死代价是生成过程中右侧画布不「逐帧」更新。方案 B加大 debounce并合并更新若仍要流式预览将STREAMING_DEBOUNCE_MS从150 → 5001000ms用requestIdleCallback带setTimeoutfallback把handleDisplayChart放到空闲时段执行流式阶段跳过validateAndFixXml只做轻量「能否 parse」检查完整校验留给最终一次方案 C减少 React 重渲染用React.memo包裹单条Message流式时只让最后一条 assistant 消息重渲染对messages使用useDeferredValue让输入和滚动优先流式文本用简单pre或节流后的 Markdown结束后再渲染完整 Markdown方案 D把 XML 处理移出主线程用Web Worker跑replaceNodes、validateAndFixXml或至少validateMxCellStructure主线程只负责把 Worker 返回的最终 XML 交给loadDiagram适合复杂大图实现成本较高但能从根上避免长同步任务。方案 E优化存储策略status streaming期间不写localStorage仅在请求结束或用户空闲时保存大 XML 用 IndexedDB或只存摘要/最近 N 条消息方案 F生产环境去掉热路径日志validateMxCellStructure、handleDisplayChart等处的console.time/console.log用if (DEBUG)包裹或仅在开发环境启用开发模式下卡顿会比生产更明显。推荐实施顺序优先级改动预期效果P0流式阶段不调用drawioRef.load()仅最终更新一次假死弹框基本可消除P1消息列表按需渲染 useDeferredValue聊天区滚动/输入更顺滑P2流式期间暂停 localStorage 写入减少周期性卡顿P3Worker 处理 XML 校验复杂图仍流畅P4去掉生产环境热路径日志开发体验改善用户侧临时缓解不改代码生成复杂图时尽量别频繁操作界面定期清空长对话和 localStorage 缓存用npm run build npm run start测生产构建比 dev Turbopack 轻很多关闭其他标签页减轻 Draw.io iframe 竞争如何验证优化是否有效Chrome DevTools →Performance录制一次完整生成流程关注长条Scripting同步 JSvalidateMxCellStructure、loadDiagram、JSON.stringify是否占主导若长任务集中在drawioRef.load和 XML 校验优先做方案 A B。