Uniapp 开发App 如何使用SSE,实现AI聊天打字机的效果

3,051 阅读2分钟

SSE(Server-Sent Events),是一种类似http的协议,实现单向多条消息分段发送的效果,服务器处理完一部分数据会先返回一段数据,等到全部内容发送完成后才会断开连接,我们也可以直接使用http去访问他,但是要等到请求完毕的时候才能一次性收到全部的内容。 打开 chatgpt.com 的控制台可以很清楚地在网络中看到EventStream加载的过程。

image.png

在h5中,可以使用 JS的 EventSource接口,但是App中,Uniapp 没有Windows对象,也没有提供SEE的接口,但可以使用RanderJsRanderJs是一个运行在视图层的js,可以使用完整的JS生态。

SSE标椎是使用GET请求,然而市场上大家基本都使用POST来把参数放到请求体中,所以我使用了github上的一个支持POST的工具fetch-event-source

下面介绍RanderJs怎么使用

使用RanderJs 代码分为3块

前面是标椎的nvue代码

  • <template>
  • <script>

后面的renderjs可以写原生的vue代码。

  • <script module="renderScript" lang="renderjs">

他们之间的通讯:

  1. 在视图层依靠:change属性,实现普通script值发生改变调用renderjs中的函数。
<view :change:props="renderScript.handleEnter" :props="props" show="false" id="renderScript"></view>
//:props="props" 绑定普通script中的props变量
//show="false" 隐藏这个标签
//:change:props="renderScript.handleEnter" props属性发生改变执行renderScript中的handleEnter函数
  1. 使用:change运行的函数会携带多个参数,第三个参数ownerInstance可以调用普通script的代码,值得一提的是,使用ownerInstance.callMethod,请不要使用<script setup>语法糖,否则调用将失败。
ownerInstance.callMethod('getSseData',参数)

完整代码

<template>

  <view :change:props="renderScript.handleEnter" :props="props" show="false" id="renderScript"></view>

  <up-input
    @confirm="handleEnter"
    v-model="content"
    placeholder="请填写您的问题"
  />
</template>

<script>

import {onLoad, onShow} from "@dcloudio/uni-app"
import {nextTick, ref, watchEffect} from "vue";
import {chatCategoryEdit, getChat, newChat, sendChat} from "../request/url";

export default {
  setup() {

    const content = ref('')
    const props = ref({})
    
    
    const handleEnter = () => {
      const question = content.value
      props.value = {
        question: question,
        data:{},
        token:'xx',
      }
    }
    
    const getSseData = (data)=>{
        console.log(data)
    }

    return{
      content,
      props,
      handleEnter,
      getSseData,
    }
  }
}


</script>

<script module="renderScript" lang="renderjs">
import {baseUrl} from "../request";
import { fetchEventSource } from '@microsoft/fetch-event-source';
export default {
  data(){
    return {
    }
  },
  methods:{
    handleEnter(newData,oldData,ownerInstance){

      if(!newData.data){
       return
      }
      const data = newData.data
      const token = newData.token
       fetchEventSource(`${baseUrl}/api/chat/chatAi`, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          'token':token
        },
        body: JSON.stringify(data),
        onmessage(ev) {
            ownerInstance.callMethod('getSseData',ev)
        },
        onclose() {
          console.log('结束连接');
          ownerInstance.callMethod('getSseData',{data:"{"data":"done"}"})
        },
        onerror(err) {
         console.log('发生错误');
          console.log(err)
        },
        async onopen(response) {
         console.log('开始连接');
        },
      });

    }
  }
}

</script>

这是我来到稀土掘金发的第一篇博客,希望可以帮到大家。