From fd8d4399d37bcd5c5beb060c9df30e5f05fa4a3d Mon Sep 17 00:00:00 2001 From: chenchun Date: Wed, 17 Dec 2025 16:03:03 +0800 Subject: [PATCH] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96markdown=E8=BE=93?= =?UTF-8?q?=E5=87=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/XMarkdownAsync/index.vue | 40 ------------------- .../components/CodeBlock/index.vue | 18 ++++++++- .../XMarkdownCore/core/components.ts | 39 ++++++++++++++++-- 3 files changed, 51 insertions(+), 46 deletions(-) delete mode 100644 Yi.Ai.Vue3/src/vue-element-plus-y/components/XMarkdownAsync/index.vue diff --git a/Yi.Ai.Vue3/src/vue-element-plus-y/components/XMarkdownAsync/index.vue b/Yi.Ai.Vue3/src/vue-element-plus-y/components/XMarkdownAsync/index.vue deleted file mode 100644 index 1c4e97a4..00000000 --- a/Yi.Ai.Vue3/src/vue-element-plus-y/components/XMarkdownAsync/index.vue +++ /dev/null @@ -1,40 +0,0 @@ - - - diff --git a/Yi.Ai.Vue3/src/vue-element-plus-y/components/XMarkdownCore/components/CodeBlock/index.vue b/Yi.Ai.Vue3/src/vue-element-plus-y/components/XMarkdownCore/components/CodeBlock/index.vue index 96deb20a..68e347b3 100644 --- a/Yi.Ai.Vue3/src/vue-element-plus-y/components/XMarkdownCore/components/CodeBlock/index.vue +++ b/Yi.Ai.Vue3/src/vue-element-plus-y/components/XMarkdownCore/components/CodeBlock/index.vue @@ -11,7 +11,8 @@ import { transformerNotationHighlight, transformerNotationWordHighlight } from '@shikijs/transformers'; -import { computed, h, reactive, ref, toValue, watch } from 'vue'; +import { computed, h, reactive, ref, toValue, watch, onUnmounted } from 'vue'; +import { debounce } from 'lodash-es'; import HighLightCode from '../../components/HighLightCode/index.vue'; import { SHIKI_SUPPORT_LANGS, shikiThemeDefault } from '../../shared'; import { useMarkdownContext } from '../MarkdownProvider'; @@ -88,16 +89,29 @@ async function generateHtml() { } } +// 使用防抖优化代码块渲染,避免SSE流式更新时频繁高亮 +const debouncedGenerateHtml = debounce(generateHtml, 100, { + leading: false, + trailing: true, + maxWait: 200 +}); + watch( () => props.raw?.content, async content => { if (content) { - await generateHtml(); + debouncedGenerateHtml(); } }, { immediate: true } ); +// 清理防抖定时器 +onUnmounted(() => { + debouncedGenerateHtml.cancel(); +}); + + const runCodeOptions = reactive({ code: [], content: '', diff --git a/Yi.Ai.Vue3/src/vue-element-plus-y/components/XMarkdownCore/core/components.ts b/Yi.Ai.Vue3/src/vue-element-plus-y/components/XMarkdownCore/core/components.ts index a9a04c2c..2c80cb3a 100644 --- a/Yi.Ai.Vue3/src/vue-element-plus-y/components/XMarkdownCore/core/components.ts +++ b/Yi.Ai.Vue3/src/vue-element-plus-y/components/XMarkdownCore/core/components.ts @@ -4,10 +4,11 @@ import type { PluggableList } from 'unified'; import type { PropType } from 'vue'; import type { CustomAttrs, SanitizeOptions, TVueMarkdown } from './types'; -import { defineComponent, shallowRef, toRefs, watch } from 'vue'; +import { defineComponent, shallowRef, toRefs, watch, onUnmounted } from 'vue'; // import { useMarkdownContext } from '../components/MarkdownProvider'; import { render } from './hast-to-vnode'; import { useMarkdownProcessor } from './useProcessor'; +import { debounce } from 'lodash-es'; export type { CustomAttrs, SanitizeOptions, TVueMarkdown }; @@ -93,12 +94,42 @@ const vueMarkdownAsyncImpl = defineComponent({ }); const hast = shallowRef(null); + const isProcessing = shallowRef(false); + const process = async (): Promise => { - const mdast = processor.value.parse(markdown.value); - hast.value = (await processor.value.run(mdast)) as Root; + // 避免重复处理 + if (isProcessing.value) return; + isProcessing.value = true; + + try { + const mdast = processor.value.parse(markdown.value); + hast.value = (await processor.value.run(mdast)) as Root; + } finally { + isProcessing.value = false; + } }; - watch(() => [markdown.value, processor.value], process, { flush: 'sync' }); + // 使用防抖优化SSE流式更新场景 + // trailing: true 确保最后一次更新会执行 + // leading: false 避免首次触发两次渲染 + const debouncedProcess = debounce(process, 50, { + leading: false, + trailing: true, + maxWait: 200 // 最长等待200ms,避免延迟过大 + }); + + watch( + () => [markdown.value, processor.value], + () => { + debouncedProcess(); + }, + { flush: 'post' } // 改为post,在DOM更新后执行,避免阻塞UI + ); + + // 清理防抖定时器 + onUnmounted(() => { + debouncedProcess.cancel(); + }); await process();