v-md-editor和SSE实现ChatGPT的打字机式输出

概述

不论是GPT还是文心一言,在回答的时候类似于打字机式的将答案呈现给我们,这样的交互一方面比较友好,另一方面,当答案比较多、生成比较慢的时候也能争取一些答案的生成时间。本文后端使用expressstream,使用SSE将结果逐渐输出,前端使用v-md-editor对返回的结果进行呈现。

实现效果

动画.gif

实现

1. 后端

const stream = require("stream");
const R = require('../utils/result')

let sse = null,
  contentId = 0,
  flag = 0;

function toDataString(data) {
  if (typeof data === "object") return toDataString(JSON.stringify(data));
  return data
    .split(/\r\n|\r|\n/)
    .map((line) => `data: ${line}\n`)
    .join("");
}

const messageStr = `JavaScript中常用的排序算法有以下几种:

1. 冒泡排序

冒泡排序是一种简单的排序算法,它重复地遍历要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。遍历数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。

\`\`\`javascript
function bubbleSort(arr) {
    var len = arr.length;
    for (var i = 0; i < len - 1; i++) {
        for (var j = 0; j < len - 1 - i; j++) {
            if (arr[j] > arr[j + 1]) {
                [arr[j], arr[j + 1]] = [arr[j + 1], arr[j]];
            }
        }
    }
    return arr;
}
\`\`\`

2. 选择排序

选择排序是一种简单直观的排序算法。它的工作原理是每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,直到全部待排序的数据元素排完。

\`\`\`javascript
function selectionSort(arr) {
    var len = arr.length;
    for (var i = 0; i < len - 1; i++) {
        var minIndex = i;
        for (var j = i + 1; j < len; j++) {
            if (arr[j] < arr[minIndex]) {
                minIndex = j;
            }
        }
        [arr[i], arr[minIndex]] = [arr[minIndex], arr[i]];
    }
    return arr;
}
\`\`\`

3. 插入排序

插入排序的工作原理是通过构造一个有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。

\`\`\`javascript
function insertionSort(arr) {
    var len = arr.length;
    for (var i = 1; i < len; i++) {
        var key = arr[i];
        var j = i - 1;
        while (j >= 0 && arr[j] > key) {
            arr[j + 1] = arr[j];
            j--;
        }
        arr[j + 1] = key;
    }
    return arr;
}
\`\`\`

4. 快速排序

快速排序是一种高效的排序算法,它的基本思想是使用分治法。

\`\`\`javascript
function quickSort(arr, left = 0, right = arr.length - 1) {
    if (left < right) {
        let pivotIndex = partition(arr, left, right);
        quickSort(arr, left, pivotIndex - 1);
        quickSort(arr, pivotIndex + 1, right);
    }
    return arr;
}

function partition(arr, left, right) {
    let pivotValue = arr[right];
    let pivotIndex = left;
    for (let i = left; i < right; i++) {
        if (arr[i] <= pivotValue) {
            [arr[i], arr[pivotIndex]] = [arr[pivotIndex], arr[i]];
            pivotIndex++;
        }
    }
    [arr[pivotIndex], arr[right]] = [arr[right], arr[pivotIndex]];
    return pivotIndex;
}
\`\`\`

5. 归并排序

归并排序是建立在归并操作上的一种有效的排序算法,效率为O(nlogn)。

\`\`\`javascript
function mergeSort(arr) {
    if (arr.length < 2) {
        return arr;
    }
    var mid = Math.floor(arr.length / 2);
    var left = arr.slice(0, mid);
    var right = arr.slice(mid);
    return merge(mergeSort(left), mergeSort(right));
}

function merge(left, right) {
    var result = [];
    while (left.length && right.length) {
        if (left[0] <= right[0]) {
            result.push(left.shift());
        } else {
            result.push(right.shift());
        }
    }
    while (left.length) {
        result.push(left.shift());
    }
    while (right.length) {
        result.push(right.shift());
    }
    return result;
}
\`\`\`

以上就是JavaScript中常见的几种排序算法,每种算法都有其适用的场景和优缺点,根据实际需求选择合适的排序算法可以提高程序的运行效率。`

const gptController = {
  getAnswer: async function (req, res, next) {
    // 触动定时触发
    let index = 0
    clearInterval(flag)
    flag = setInterval(() => {
      const r = new R();
      if(index < messageStr.length) {
        const message = {
          data: r.ok({
            msg: messageStr.slice(0, index + 1)
          }),
          event: "dynamicUpdate", // 事件类型,需要客户端添加对应的事件监听
          id: ++contentId,
          retry: 2000,
        };
        sse?.write(message);
        index += 1
      } else {
        const message = {
          data: r.ok({
            msg: 'end'
          }),
          event: "dynamicUpdate", // 事件类型,需要客户端添加对应的事件监听
          id: ++contentId,
          retry: 2000,
        };
        sse?.write(message);
        clearInterval(flag)
      }
    }, 60);

    res.writeHead(200, {
      "Content-Type": "text/event-stream; charset=utf-8",
    });
    sse = new stream.Transform({ objectMode: true });

    sse._transform = (message, encoding, callback) => {
      if (message.comment) sse.push(`: ${message.comment}\n`);
      if (message.event) sse.push(`event: ${message.event}\n`);
      if (message.id) sse.push(`id: ${message.id}\n`);
      if (message.retry) sse.push(`retry: ${message.retry}\n`);
      if (message.data) sse.push(toDataString(message.data));
      sse.push("\n");
      callback();
    };
    sse.write(":ok\n\n");
    sse.pipe(res);
  },
};

module.exports = gptController;

2. 前端实现

在主入口引入v-md-editor

// main.js
import VMdPreview from '@kangc/v-md-editor/lib/preview';
import vuepressTheme from '@kangc/v-md-editor/lib/theme/vuepress.js';
import createCopyCodePlugin from '@kangc/v-md-editor/lib/plugins/copy-code/index';
import '@kangc/v-md-editor/lib/style/base-editor.css';
import '@kangc/v-md-editor/lib/style/preview.css';
import '@kangc/v-md-editor/lib/plugins/copy-code/copy-code.css';
import '@kangc/v-md-editor/lib/theme/style/vuepress.css';
import '@kangc/v-md-editor/lib/style/codemirror-editor.css';
import '@kangc/v-md-editor/lib/theme/style/github.css';
import createLineNumbertPlugin from '@kangc/v-md-editor/lib/plugins/line-number/index';
import '@kangc/v-md-editor/lib/plugins/tip/tip.css';
import Prism from 'prismjs';

VMdPreview.use(vuepressTheme, { Prism });
VMdPreview.use(createCopyCodePlugin());
VMdPreview.use(createLineNumbertPlugin());

const app = createApp(App)
app.use(VMdPreview).mount('#app')

页面引用v-md-preview

<template>
  <div class="app">
    <v-md-preview :text="mdText"></v-md-preview>
  </div>
</template>

<script>
export default {
  data() {
    return {
      mdText: "",
    };
  },
  mounted() {
    const that = this
    const es = new EventSource("http://localhost:8080/gpt/sse");
    es.addEventListener("dynamicUpdate", (e) => {
      const data = JSON.parse(e.data);
      const msg = data.data.msg;
      if (msg === "end") {
        es.close();
      } else {
        that.mdText = msg
      }
    });
    es.onopen = () => {
      console.log("已开启。。。");
    };
    es.onmessage = (e, me) => {
      console.log("默认推送:" + e.data);
    };
    es.onerror = (err) => {
      console.log(err);
    };
  },
};
</script>

<style scoped>
.app {
  width: 100%;
  height: 100%;
}
</style>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/634601.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

WXML模板语法-数据绑定

1.数据绑定的基本原则 (1)在data中定义数据 (2)在WXML中使用数据 2.在data页面中定义数据&#xff1a;在页面对应的.js文件中&#xff0c;把数据定义在data对象中即可 &#xff08;这里打错了 应该是数组类型的数据... 报意思啊&#xff09; 3.Mustache语法的格式 把data中的…

容器组件:栅格布局,侧边栏容器(HarmonyOS学习第四课【4.5】)

栅格布局 栅格布局可以为布局提供规律性的结构&#xff0c;解决多尺寸多设备的动态布局问题&#xff0c;保证不同设备上各个模块的布局一致性。 栅格容器组件&#xff0c;仅可以和栅格子组件(GridCol)在栅格布局场景中使用。 说明 该组件从API Version 9开始支持。后续版本…

WordPress主题 7B2 PRO 5.4.2 免授权开心版源码

本资源提供给大家学习及参考研究借鉴美工之用&#xff0c;请勿用于商业和非法用途&#xff0c;无任何技术支持&#xff01; WordPress主题 7B2 PRO 5.4.2 免授权开心版源码 B2 PRO 5.4.2 最新免授权版不再需要改hosts&#xff0c;和正版一样上传安装就可以激活。 直接在Word…

计算机精选期刊特辑

文章目录 一、征稿简介二、合作期刊三、投稿咨询四、咨询 一、征稿简介 艾思科蓝依托互联网信息与数据库技术、整合渠道与合作资源&#xff0c;提供EI/SCI/SCIE/SSCI期刊论文的内容审查、发表支持等服务。艾思科蓝与多所知名出版社达成战略合作关系&#xff0c;持续开展合作征…

【Unity】Rider无法调试团结引擎

近在学习unity&#xff0c;代码编辑器选择了熟悉的idea系列&#xff0c;C# 对应的编辑器 rider 之前在使用unity的时候&#xff0c;可以直接使用 Rider进行调试&#xff0c;很方便 但是后来又安装了团结引擎&#xff0c;在启动调试的时候断点总是无法激活 在点击调试按钮的时…

Vue文本溢出如何自动换行

css新增 word-break: break-all; word-wrap: break-word;

如何在没有密码或Face ID的情况下解锁iPhone

iPhone 是一款以其一流的安全功能而闻名的设备&#xff0c;包括面容 ID 和密码。但是&#xff0c;你有没有想过&#xff0c;如果没有这些安全措施&#xff0c;你是否可以解锁iPhone&#xff1f;无论您是忘记了密码&#xff0c;Face ID不起作用&#xff0c;还是只是对其他方法感…

浅析declval关键字

浅析 declval 关键字 文章目录 浅析 declval 关键字前言declval 的基本概念declval 的工作原理declval 的实际应用案例总结 前言 ​ 在现代C编程中&#xff0c;std::declval是一个非常有用的工具&#xff0c;它允许我们在不实例化对象的情况下使用其类型。这在模板元编程中尤其…

Git系列:git rm 的高级使用技巧

&#x1f49d;&#x1f49d;&#x1f49d;欢迎莅临我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:「stormsha的主页」…

实用css整理

网页一键变灰 body{filter: grayscale(1); } 一般用于特殊时期&#xff0c;网页变灰&#xff0c;只需要给body标签添加这行样式代码。 一键换主题色 body {filter: hue-rotate(45deg);} 给body标签设置这个属性样式&#xff0c;改变角度看看效果吧。 设置字母大小写 p {t…

ESP32开发环境搭建Windows VSCode集成Espressif IDF插件开发环境搭建 IDF_V5.2.1

一、安装Visual Studio Code 下载地址&#xff1a;Download Visual Studio Code - Mac, Linux, Windows 打开上方链接&#xff0c;选择页面中的Windows版本&#xff0c;单击下载 将下载好的VSCodeUserSetup-x64-1.89.1.exe。单击右键&#xff0c;选择以管理员身份运行&#xf…

网络安全之BGP详解

BGP&#xff1b;边界网关协议 使用范围&#xff1b;BGP范围&#xff0c;在AS之间使用的协议。 协议的特点&#xff08;算法&#xff09;&#xff1a;路径矢量型&#xff0c;没有算法。 协议是否传递网络掩码&#xff1a;传递网络掩码&#xff0c;支持VLSM&#xff0c;CIDR …

【制作100个unity游戏之26】unity2d横版卷轴动作类游戏7(附带项目源码)

最终效果 系列导航 文章目录 最终效果系列导航前言血条 能量条UI配置画布绘制血条 能量条UI 头像框 延迟虚血源码参考完结 前言 欢迎来到【制作100个Unity游戏】系列&#xff01;本系列将引导您一步步学习如何使用Unity开发各种类型的游戏。在这第26篇中&#xff0c;我们将探索…

ESP8266 作为客户端 透传的实验

配置流程 1.配置WIFI模式 ATCWMODE1 配置成Station模式 2.完成配置后重启 ATRST 重启 3.连接路由器 ATCWJAP“777”,“123456lzg” 连接wifi 4.配置单路连接模式 ATCIPMUX0 5.开启透传模式 ATCIPMODE1 6.建立TCP连接 ATCIPSTART“TCP”,“172.20.10.10”,8081 //注意服务器和es…

鸿蒙OS开发:【一次开发,多端部署】(典型布局场景)

典型布局场景 虽然不同应用的页面千变万化&#xff0c;但对其进行拆分和分析&#xff0c;页面中的很多布局场景是相似的。本小节将介绍如何借助自适应布局、响应式布局以及常见的容器类组件&#xff0c;实现应用中的典型布局场景。 布局场景实现方案 开发前请熟悉鸿蒙开发指导…

[机器学习聚类算法实战-1] | Scikit-Learn工具包进阶指南:机器学习聚类算法之层次聚类、特征集聚、均值移位聚类、k-均值聚类实战分析

&#x1f3a9; 欢迎来到技术探索的奇幻世界&#x1f468;‍&#x1f4bb; &#x1f4dc; 个人主页&#xff1a;一伦明悦-CSDN博客 ✍&#x1f3fb; 作者简介&#xff1a; C软件开发、Python机器学习爱好者 &#x1f5e3;️ 互动与支持&#xff1a;&#x1f4ac;评论 &…

面试八股之线程篇2.7——线程中的并发锁篇——死锁与并发程序的问题

文章目录 2.6 synchronized和Lock有什么区别 ?2.7 死锁产生的条件是什么&#xff1f;2.8 如何进行死锁诊断&#xff1f;2.10 ConcurrentHashMap&#xff08;1&#xff09; JDK1.7中concurrentHashMap&#xff08;2&#xff09; JDK1.8中concurrentHashMap 2.11 导致并发程序出…

如何将Docker容器打包并在其他服务器上运行

如何将Docker容器打包并在其他服务器上运行 我会幻想很多次我们的相遇&#xff0c;你穿着合身的T恤&#xff0c;一个素色的外套&#xff0c;搭配一条蓝色的牛仔裤&#xff0c;干净的像那天空中的云朵&#xff0c;而我&#xff0c;还是一个的傻傻的少年&#xff0c;我们相识而笑…

洗地机品牌哪个牌子好?实力派洗地机品牌TOP10榜单

洗地机依靠其洗、拖、吸、烘为一体的功能&#xff0c;能高效的完成地面清洁的工作&#xff0c;深受大家的喜爱。但是洗地机的型号越来越多&#xff0c;功能也越来越多&#xff0c;对于不想花大价钱&#xff0c;又想要高性价比的精致人群来说实在不友好&#xff0c;所以笔者今天…

QMT如何编写策略获取沪深指数数据?(附开通QMT全攻略)

获取指数代码列表 提示 为了获取指数合约列表,首先需要使用函数get_sector_list来获取需要查询的指数索引。具体的索引信息可以通过键入您感兴趣的索引名&#xff08;例如&#xff1a;"沪深指数"或"上证指数"&#xff09;等获得。接下来&#xff0c;通过…