大模型流式输出卡住了,教你一招搞定

1,642 阅读2分钟

大模型流式输出卡住了,教你一招搞定

前言

腊月二十四,南方小年夜,苦逼的前端程序媛还在搬砖,怎一个惨字了得。沸点都刷烂了,这不都沦落到自己写了😂

就这,产品经理还不放过我。

“前端,问答的时候复制按钮点不动啦!赶紧看看!”

好家伙,岂止是复制按钮用不了,页面直接卡住了好吗?

一通头脑风暴,发现是流式输出,界面频繁重绘导致的性能问题。

简简单单看个代码

<script setup lang="ts">
import { ref } from 'vue'
import MarkdownIt from 'markdown-it'
import data from './README.md?raw' // ?raw表示将资源引入为字符串
import { ElButton } from 'element-plus'
const md = new MarkdownIt()
let str = ref<string>(''),
  content = ref(),
  index = ref<number>(0)

function mockStream() {
  let timer = setInterval(() => {
    if (index.value >= data.length) {
      clearInterval(timer)
      return
    }
    str.value += data.substring(index.value, index.value + 1)
    content.value = md.render(str.value)
    index.value++;
  }, 100)
}
</script>

<template>
  <div>
    <ElButton type="primary" @click="mockStream">START BASE RENDER</ElButton>
    <div v-html="content"></div>
  </div>
</template>

就会发现,这界面咱是选也选不了,点也点不动。究其原因,content一直在重新赋值导致dom频繁重绘。

想法

咱就是说,有没有什么办法能够让dom不全部重绘呢?

⭐⭐⭐增量渲染⭐⭐⭐

dom转换成DomTree数组,依托vuefor循环更新机制去做增量渲染。

开搞

1. 把dom转化成DomTree

使用htmlparser2parseDocument函数,一行代码就ok

content.value = parseDocument(md.render(str.value)).children

2. 递归DomTree,转成dom

import { defineComponent, h } from "vue";

// 递归渲染组件
export const RenderWrap = defineComponent({
    name'RenderWrap',
    props: {
        node: {
            typeObject,
            requiredtrue,
        }
    },
    setup(props) {
        return () => {
            const { node } = props;
            if (node.type === 'text') {
                return node.data
            } else {
                return h(
                    node.tagName, 
                    { ...node.attribs }, 
                    node.children.map((child: any, index: number) => 
                        h(RenderWrap, { node: child, key: index })
                    )
                )
            }
        }
    }
})

3. 站在尤大的肩膀上

🐕🐕🐕哈哈哈🐕🐕🐕

其实是用v-for遍历生成的DomTree,使用RenderWrap组件再转成dom,借助vue的增量更新算法。

<RenderWrap v-for="(node, index) in content" :node="node" :key="index" />

这谁不得夸我是个大聪明啊😂

结语

这种方案虽说能够降低页面重绘的区域,但是遇到内容超多的段落时,整段都会重绘。

友友们要是有其他方案,可以交换学习一下哦😊

我是胡邹邹,持续潜水,偶尔冒泡。

最后,新人没有赞真的很容易放弃欸 /(ㄒoㄒ)/~~