修复: AR-1 流式 Markdown 全量重解析掉帧

renderMd 加 isStreaming 参数:流式态(streaming 中 currentText)纯文本(escapeHtml+<br>)不 parse,避免每 delta 全量 marked.parse+DOMPurify.sanitize 致主线程阻塞掉帧;不缓存流式文本(每 delta 变,污染缓存);完成后再走 markdown 一次 parse
来源 aichat审查报告 第一章 H1 P0;vue-tsc 0 err
This commit is contained in:
2026-06-14 22:16:07 +08:00
parent f82dd8ba90
commit f58743efbc

View File

@@ -197,7 +197,7 @@
class="ai-msg-bubble ai-msg-bubble--ai ai-md"
:class="{ 'ai-msg-bubble--error': msg.isError }"
>
<div v-html="renderMd(isLastAi(msg) && store.state.currentText ? store.state.currentText : msg.content)"></div>
<div v-html="renderMd(isLastAi(msg) && store.state.currentText ? store.state.currentText : msg.content, isLastAi(msg) && store.state.streaming)"></div>
<span v-if="isLastAi(msg) && isViewingGenerating" class="ai-cursor"></span>
</div>
<!-- 流式开始但尚无文本时显示光标 -->
@@ -344,14 +344,16 @@ function escapeHtml(text: string): string {
return text.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;')
}
function renderMd(text: string): string {
function renderMd(text: string, isStreaming = false): string {
if (!text) return ''
// 流式态纯文本(AR-1):避免每 delta 全量 marked.parse+sanitize 致主线程阻塞掉帧;
// 不缓存流式文本(每 delta 变,缓存无收益且污染),仅 escapeHtml + 换行转 <br>;完成后再走 markdown
if (isStreaming || !mdReady.value || !_marked || !_purify) {
return escapeHtml(text).replace(/\n/g, '<br>')
}
const cached = _mdCache.get(text)
if (cached !== undefined) return cached
// 依赖 mdReady 使加载完成后自动重渲染;未就绪时先以纯文本兜底
const html = (!mdReady.value || !_marked || !_purify)
? escapeHtml(text)
: _purify.sanitize(_marked.parse(text) as string)
const html = _purify.sanitize(_marked.parse(text) as string)
if (_mdCache.size > 200) _mdCache.clear()
_mdCache.set(text, html)
return html