在这里插入图片描述
前端使用vue

1.逐字输出 闪动css样式

<span id="response_row" class="result-streaming">{{ item.assistantContent }}</span>
.result-streaming:after {
  -webkit-animation: blink 1s steps(5, start) infinite;
  animation: blink 1s steps(5, start) infinite;
  content: "▋";
  margin-left: 0.25rem;
  vertical-align: baseline;

2.使用fetch/eventSource/fetchEventSource进行sse流式处理

先贴最后成功使用的
使用fetchEventSource方法
参考代码:https://blog.csdn.net/cuiyuchen111/article/details/129468291
参考/下载文档:https://www.npmjs.com/package/@microsoft/fetch-event-source?activeTab=readme

以下为后端接口要求
在这里插入图片描述
前端代码

<p v-if="item.requestFlag" class="content robot_content"><span id="response_row" class="result-streaming">{{ item.assistantContent }}</span></p>
<p class="content robot_content"><span v-html="item.assistantContent"></span></p>
 async getResponseFromAPI() {
          const that = this;
          this.sendLoading = true;
          // 用户提问时间
          let userTime = that.getNowTime();
          const form = JSON.parse(JSON.stringify(this.form));
          console.log(form, "请求里的form");
          //物理添加 页面
          that.conversations.push({
            userContentId: "",
            userContent: form.prompt,
            userContentDatetime: userTime,
            assistantContentId: "",
            assistantContent: "",
            assistantContentDatetime: userTime,
            requestFlag: true,
          });
          // 对话请求
          const abortController = new AbortController();
          let formData = new FormData();
          formData.append("chatid", this.currentChatId);
          formData.append("clientid", form.clientid);
          formData.append("prompt", form.prompt);
          const url = "xxxxx";
          const headers = new Headers();
          const body = formData;
          const eventSource = fetchEventSource(url, {
            method: "POST",
            headers,
            body,
            signal: abortController.signal,
            onmessage(e) {
              that.handleScrollBottom();
              that.form.prompt = "";
              const response_row = document.getElementById("response_row");
              console.log(e.data);
              let res = JSON.parse(e.data);
              let index = that.conversations.length - 1;
              if (res.message == "[DONE]") {
                res.data = JSON.parse(res.data);
                console.log(res.data);
                let obj = {
                  userContentId: res.data.userContentId,
                  userContent: res.data.userContent,
                  userContentDatetime: userTime,
                  assistantContentId: res.data.assistantContentId,
                  assistantContent: res.data.assistantContent,
                  assistantContentDatetime: that.getNowTime(),
                  requestFlag: false,
                console.log(obj);
                that.$set(that.conversations, index, obj);
                that.sendLoading = false;
                abortController.abort();
                eventSource.close();
                console.log("我是结束!!");
              } else {
                var content = res.data;
                response_row.innerText += content;
                // console.log(content)
                // if (content.includes("[ENTRY]")) {
                //   content = content.replaceAll("[ENTRY]", "\n");
            onclose() {
              console.log("close");
              that.sendLoading = false;
              abortController.abort();
              eventSource.close();
            onerror(error) {
              let index = that.conversations.length - 1;
              that.conversations.splice(index, 1);
              that.sendLoading = false;
              console.log("error", error);
              abortController.abort();
              eventSource.close();
          });

遇到的问题:
1.只调用一次事件 但fetch请求发送了两次或多次且终止失败

//按照fetchEventSource文档内的写法 请求暂停无效
const ctrl = new AbortController();
fetchEventSource('/api/sse', {
    method: 'POST',
    headers: {
        'Content-Type': 'application/json',
    body: JSON.stringify({
        foo: 'bar'
    }),
    signal: ctrl.signal,
});
//而后看到一种说法,fetchEventSource是针对EventSource API的,而不是xhr或fetch API
//因此定义EventSource存储接口所返回的数据 使用EventSource的暂停方法 =》 fetchEventSource暂停成功
const eventSource = fetchEventSource(url, {
            method: "POST",
            headers,
            body,
            signal: abortController.signal,
            onmessage(e) {
                eventSource.close();
            onclose() {
              eventSource.close();
            onerror(error) {
              eventSource.close();
          });

以下为fetch/eventSource使用过程中遇到的问题

1.使用fetch方式进行sse流式处理
优点:可以使用post请求
缺点:获取到的数据处理困难 获取事件返回格式或有错误
参考代码:https://blog.csdn.net/betterAndBetter_/article/details/129900233
     http://681314.com/A/YaHyYpjoPF

async function send() {
    const input = document.getElementById("input").value;
    const output = document.getElementById("output");
    output.innerText = "";
    const url = "/api/stream";
    const data = { "Prompt": input };
//直接获取 Fetch 的response, 无法使用 await的话, Promise的方式也是可以的。
    const response = await fetch(url, {
        method: "POST",
        body: JSON.stringify(data),
        headers: {
            "Content-Type": "application/json"
	//获取UTF8的解码
    const encode = new TextDecoder("utf-8");
	//获取body的reader
	  const reader = response.body.getReader();
	// 循环读取reponse中的内容
    while (true) {
        const { done, value } = await reader.read();
        if (done) {
            break;
	// 解码内容
        const text = encode.decode(value);
	// 当获取错误token时,输出错误信息
        if (text === "<ERR>") {
            output.innerText = "Error";
            break;
        } else {
	// 获取正常信息时,逐字追加输出
            output.innerText += text;

【记得补截图】

2.使用eventSource进行sse流式处理
优点:获取到的数据格式规范 易处理
缺点:无法使用post请求
参考b站视频:https://www.bilibili.com/video/BV1QA411C7mN/?spm_id_from=333.880.my_history.page.click&vd_source=384646ea9baa6985ceb5331bff5b87b0

var rsource = (this.rsource = new EventSource(
        `/api/chat/repeat/${this.cid}`
      ));
      rsource.addEventListener("open", function () {
        console.log("connect");
      });
      //如果服务器响应报文中没有指明事件,默认触发message事件
      rsource.addEventListener("message", function (e) {
        console.log(`resp:(${e.data})`);
        var rconv = that.conversation[that.conversation.length - 1];
        if (e.data == "[DONE]") {
          rsource.close();
          rconv["loading"] = false;
          that.convLoading = false;
          that.refrechConversation();
          that.rsource = undefined;
          return;
        var content = e.data;
        if (content.includes("[ENTRY]")) {
          content = content.replaceAll("[ENTRY]", "\n");
        // 滚动到最下面
        that.handleScrollBottom();
        var idx = rconv.idx;
        rconv["speeches"][idx] += content;
        that.refrechConversation();
      });
      //发生错误,则会触发error事件
      rsource.addEventListener("error", function (e) {
        console.log("error:" + e.data);
        rsource.close();
        that.rsource = undefined;
      });

由于eventSource获取到的数据比fetch流畅许多,所以研究过eventSource能否使用post请求,使用过以下代码,但失败了
在这里插入图片描述
3.fetch和eventSource同时使用
优点:可以很顺利的请求并且获取到数据
缺点:fetch支持post eventSource不支持post 对接口请求方式有要求 几乎不太能兼容

// 获取表单元素
const form = document.querySelector('#my-form');
// 监听表单提交事件
form.addEventListener('submit', (event) => {
  event.preventDefault(); // 阻止默认提交行为
  const formData = new FormData(form); // 创建 FormData 对象
  // 发送 POST 请求并接收 SSE 流式输出
  fetch('/api/submit-form', {
    method: 'POST',
    body: formData
  }).then((response) => {
    // 如果请求成功,则创建 EventSource 对象监听 SSE 输出
    if (response.ok) {
      const eventSource = new EventSource('/api/stream');
      eventSource.onmessage = (event) => {
        const data = JSON.parse(event.data);
        console.log(data); // 处理接收到的数据
      eventSource.onerror = (error) => { // 监听错误事件
        console.error(error);
  }).catch((error) => {
    console.error(error);
  });
});
				
vue使用EventSource mounted() { if(typeof (EventSource) !== 'undefined') { //支持eventSource var postURL = 'http……'; this.source = new EventSource(postURL); let self = this;//因EventSource中this的指向变了,所以要提前存储一下 this.source.onopen = function
fetch/xhr和其他请求(如axios、ajax等)之间的主要区别在于它们的使用方式和一些功能上的不同。 首先,fetch是一种基于Promise的现代化网络请求API,而xhr是一种传统的XMLHttpRequest对象。fetch API基于新的web标准,可以更好地处理请求和响应,支持更多现代化的功能,而xhr则是老旧的方式。 另一个区别在于fetch API返回的是一个Promise对象,可以使用Promise的链式调用和async/await语法,非常方便处理异步操作。而xhr则需要使用回调函数来处理异步操作,代码结构可能相对复杂。 此外,fetch API默认情况下不会携带cookie信息,需要设置credentials属性为"include"才能发送cookie。而xhr默认会发送cookie信息,需要手动设置xhr.withCredentials属性为true来禁止发送cookie。 另外,fetch API在默认情况下只会拒绝请求错误的状态码(如404或500等),而不会拒绝其他的网络错误(如网络超时)。这意味着需要手动检查并处理网络错误。而xhr则可以通过onerror事件来处理所有类型的网络错误。 最后,fetch API在使用上可能相对简单,语法更加直观。而xhr则相对复杂,需要手动设置请求头、处理请求和响应等。 总结起来,fetch/xhr和其他请求的主要区别在于使用方式、功能支持和代码结构等方面。fetch提供了更现代化、更简洁的API,支持Promise和async/await语法,但xhr仍然是一种可靠和广泛使用的老旧方式。 ### 回答2: fetch和XMLHttpRequest (XHR) 是两种常用的网络请求方式,它们之间有以下几点区别: 1. 语法使用fetch是浏览器提供的一种基于Promise的现代API,通过使用ES6的语法,使用更加简洁。XHR则是使用传统的回调函数的方式,使得代码可读性较差。 2. URL和参数:在使用fetch时,可以将URL和请求参数作为参数传递给fetch函数。XHR需要手动构建URL和参数,增加了额外的代码。 3. 跨域请求:fetch默认不会将跨域的cookie发送到目标服务器,需要设置`credentials`配置项为"include"。而XHR请求跨域时会自动携带cookie。 4. 请求头配置:fetch通过调用`Headers`对象来设置请求头信息,更加方便。XHR则需要通过`setRequestHeader`方法来设置。 5. 请求/响应:使用fetch时,需要通过两个Promise分别处理请求和响应,使得代码更加清晰。XHR则需要编写多个回调函数来处理请求与响应,并引发回调地狱问题。 6. 取消请求:使用fetch时,可以使用`AbortController`对象来取消正在进行的请求。XHR则需要手动使用`abort`方法来取消请求。 综上所述,fetch相比XHR具有更好的语法简洁性、使用更加方便、处理异步操作更加优雅等优点,但XHR在兼容性和一些特殊场景处理方面表现更好。所以,在选择网络请求方式时,根据具体的场景需求进行选择。 ### 回答3: fetch和XHR(XMLHttpRequest)都是用于发送HTTP请求并获取响应的JavaScript API。它们的主要区别如下: 1. 语法:fetch使用更简洁的语法,基于Promise实现,而XHR使用回调函数来处理异步请求。 2. 兼容性:XHR在所有主流浏览器中都得到了广泛支持,包括较旧的版本。而fetch在一些旧版本的浏览器中可能不被完全支持,需要使用polyfill进行兼容。 3. 请求和响应对象:XHR通过实例化XMLHttpRequest对象,并使用该对象来发送请求和接收响应。而fetch直接使用全局fetch函数来发送请求并返回一个Promise对象,该对象包含响应的相关信息。 4. 请求和响应的处理:XHR可以通过各种回调函数来处理请求和响应,包括onload、onerror和onprogress等。而fetch使用then方法链式调用来处理请求和响应,也可以使用catch捕获错误。 5. 默认不接受跨域请求:在默认情况下,XHR在同源策略下才能发送跨域请求。而fetch在跨域请求时,默认不发送站点的身份验证cookie和HTTP认证信息,需要手动设置credentials为include。 总的来说,fetch相对于XHR提供了更简洁的语法和更强大的功能,并且更符合现代JavaScript的编码风格。但是在兼容性方面,XHR更广泛支持,可以在更多的浏览器环境中使用let eventSource = fetchEventSource() eventSource.close() eventSource.close is not a function 请问又遇到过这种找不到close方法吗,现在没法关闭连接,只能通过new AbortController().abort()取消请求,但是这样不会进入onclose回调里
vue集成百度地图(含搜索框获取经纬度) Li123456__7: ruleForm: {}, 要给个空对象要不报错 vue 下载图片 再见 1998: 还是非同源用了还是跨域了。。。