前端实现流式输出《后端返回Markdown格式文本,前端输出类似于打字的那种》

一、使用插件

插件名称:marked

版本:15.0.11

安装插件:npm install marked@15.0.11

作用:marked 是一个用于将 Markdown 语法转换为 HTML 的 JavaScript 库

插件2名称:dompurify

版本:3.2.5

安装插件:npm install dompurify@3.2.5

作用:DOMPurify 是一个专门用于防止 XSS(跨站脚本攻击)的快速、易用且强大的工具。

二、代码实现

javascript 复制代码
  <div v-html="MdHtml"></div>
  import { marked } from 'marked'
  import DOMPurify from 'dompurify'

// 推荐使用fetch 
  function Fetch({ url, method, data }) {
  return new Promise(async (resolve, reject) => {
    window.controller = new AbortController();
    let sig = window.controller.signal;
    try {
      var response = await fetch(url, {
        headers: {
          'Authorization': 'Bearer ' + gettoken(),
          'Content-Type': 'application/json'
        },
        method: method,
        body: JSON.stringify(data),
        signal:sig 
      })
      if (response.status != 200) {
          reject(response)
      } else {
        resolve(response)
      }
    } catch (err) {
      reject(err)
    }
  })
}
const MdHtml = ref('')
// 发送网络请求
const API = async () => {
    let param = {
      "id": 'xxx',
    }
    let outText = '' // 用于存储所有接收到的数据块的文本。
    Fetch({
        url: `/xxx/xxx/xxx`,
        method: 'post',
        data: param
      }).then(async res => {
      // res.body 是一个可读的流
      const reader = res.body.getReader() // getReader 方法返回一个读取器(ReadableStreamDefaultReader),用于读取流中的数据块
      const dec = new TextDecoder() //  用于将二进制数据解码为文本(解码当前数据块)
      while (true) {
        /**
         * reader.read() 返回一个 Promise,解析为一个对象,包含 done 和 value。
         * done 表示流是否已经结束。
         * value 是当前数据块的二进制数据。
         * 如果 done 为 true,表示流已经结束,调用 getComparsonHistoryNet() 并退出循环
        */
        const { done, value } = await reader.read()
        if (done) { // 结束后调用别的函数
          break
        }
        const ck = dec.decode(value, { stream: true }) // stream: true 表示这是一个流式解码,适用于部分数据。
        outText += ck// 解码后的文本块添加到 outText 中
        const outHtml = marked.parse(outText) // 使用 marked 库将 outText 转换为 HTML
        MdHtml.value = DOMPurify.sanitize(outHtml) //使用 DOMPurify 库对生成的 HTML 进行安全处理,防止 XSS 攻击 (将处理后的 HTML 赋值给 MdHtml.value,通常用于更新 Vue 组件的响应式数据)
  };

onMounted(() => {
    API()
})

三、为什么用 fetch 但是不用 axios

在前端实现流式输出(例如处理服务器发送的事件流,如 SSE 或者分块传输的响应),fetch API 相比 axios 更受推荐的原因主要在于其对流式数据处理的原生支持以及更灵活的控制能力。以下是具体原因:

1. 原生支持流处理

fetch 提供了对 ReadableStream 的直接支持,允许你通过 .body 属性访问响应体作为一个可读流。这意味着你可以逐块读取数据,并且可以在数据到达时立即开始处理,而不是等待整个响应完成。

javascript 复制代码
fetch('your-stream-endpoint')
  .then(response => {
    const reader = response.body.getReader();
    const decoder = new TextDecoder('utf-8');
    
    function read() {
      reader.read().then(({ done, value }) => {
        if (done) {
          console.log('Stream complete');
          return;
        }
        console.log(decoder.decode(value, { stream: true }));
        read(); // Continue reading
      });
    }

    read();
  });

axios 默认情况下不支持流式处理,尽管可以通过配置 responseType: 'stream' 来尝试获取流数据,但这通常仅限于 Node.js 环境下使用,并且对于浏览器端的支持不如 fetch 自然和直接。

四、总结

当涉及到流式数据处理时,fetch 因为其对 ReadableStream 的原生支持、更轻量的特点以及更高的灵活性,成为了更优的选择。然而,这并不意味着 axios 不好,它在很多其他场景下仍然是一个强大的 HTTP 客户端工具,尤其是在需要高级功能如请求/响应拦截、自动转换 JSON 数据等方面表现突出。选择哪个工具应基于具体的项目需求来决定。如果项目中大量使用了 axios 并且对流式处理的需求不多,可能继续使用 axios 加上适当的扩展也是可行的。但如果主要是为了处理流式数据,那么 fetch 将是更好的选择。

相关推荐
低保和光头哪个先来5 分钟前
场景2:Vue Router 中 query 与 params 的区别
前端·javascript·vue.js·前端框架
q***952219 分钟前
SpringMVC 请求参数接收
前端·javascript·算法
|晴 天|21 分钟前
Vite 为何能取代 Webpack?新一代构建工具的崛起
前端·webpack·node.js
带只拖鞋去流浪25 分钟前
迎接2026,重新认识Webpack5
前端·webpack
HIT_Weston40 分钟前
43、【Ubuntu】【Gitlab】拉出内网 Web 服务:静态&动态服务
前端·ubuntu·gitlab
LucidX1 小时前
Web——反向代理、负载均衡与 Tomcat 实战部署
前端·tomcat·负载均衡
sen_shan1 小时前
《Vue项目开发实战》第八章:组件封装--vxeGrid
前端·javascript·vue.js
五点六六六1 小时前
双非同学校招笔记——离开字节入职小📕
前端·面试·程序员
IT_陈寒1 小时前
Redis实战:5个高频应用场景下的性能优化技巧,让你的QPS提升50%
前端·人工智能·后端
2***57421 小时前
Vue项目国际化实践
前端·javascript·vue.js