diff --git a/packages/ui/src/components/CodeBlock.tsx b/packages/ui/src/components/CodeBlock.tsx index 1f23a17..68eb014 100644 --- a/packages/ui/src/components/CodeBlock.tsx +++ b/packages/ui/src/components/CodeBlock.tsx @@ -4,7 +4,7 @@ * 代码块组件,支持语法高亮和一键复制 */ -import { useState, useEffect } from 'react'; +import { useState, useEffect, useRef } from 'react'; import { Copy, Check } from 'lucide-react'; import { codeToHtml } from 'shiki'; import { cn } from '../utils/cn'; @@ -18,37 +18,42 @@ interface CodeBlockProps { export function CodeBlock({ code, language = 'text', className }: CodeBlockProps) { const [copied, setCopied] = useState(false); const [highlightedHtml, setHighlightedHtml] = useState(''); - const [isLoading, setIsLoading] = useState(true); + // 使用 ref 跟踪上一次高亮的代码,避免不必要的重新高亮 + const lastHighlightedCode = useRef(''); + const highlightTimer = useRef | null>(null); - // 语法高亮 + // 语法高亮 - 使用防抖避免频繁高亮导致闪烁 useEffect(() => { - let cancelled = false; + // 清除之前的定时器 + if (highlightTimer.current) { + clearTimeout(highlightTimer.current); + } - async function highlight() { + // 如果代码没变,不需要重新高亮 + if (code === lastHighlightedCode.current && highlightedHtml) { + return; + } + + // 使用防抖:等待 150ms 后再进行高亮(流式输出时减少高亮次数) + highlightTimer.current = setTimeout(async () => { try { const html = await codeToHtml(code, { lang: language, theme: 'github-dark', }); - if (!cancelled) { - setHighlightedHtml(html); - } + lastHighlightedCode.current = code; + setHighlightedHtml(html); } catch { // 如果语言不支持,回退到纯文本 - if (!cancelled) { - setHighlightedHtml(`
${escapeHtml(code)}
`); - } - } finally { - if (!cancelled) { - setIsLoading(false); - } + lastHighlightedCode.current = code; + setHighlightedHtml(`
${escapeHtml(code)}
`); } - } - - highlight(); + }, 150); return () => { - cancelled = true; + if (highlightTimer.current) { + clearTimeout(highlightTimer.current); + } }; }, [code, language]); @@ -82,17 +87,17 @@ export function CodeBlock({ code, language = 'text', className }: CodeBlockProps - {/* 代码内容 */} + {/* 代码内容 - 始终显示高亮后的内容,未高亮时显示纯文本 */}
- {isLoading ? ( -
-            {code}
-          
- ) : ( + {highlightedHtml ? (
+ ) : ( +
+            {code}
+          
)}