在 AI 聊天机器人、实时日志、大屏监控、长文本渲染等场景中,流式输出(Streaming) 已经成为标配能力。它让页面不必等待全部数据返回,而是收到一块、渲染一块,极大降低首屏等待时间、减少内存占用、提升用户体验。

本文从原理 → API → 多框架实现 → 优化 → 排错,完整覆盖前端流式输出全链路,可直接用于生产环境。

目录

一、流式输出核心原理

1.1 什么是流式输出?

1.2 核心优势

1.3 关键技术支撑

二、三种主流方案对比

三、原生 JavaScript 实现(最通用)

3.1 Fetch + ReadableStream 标准实现

四、SSE 极简实现(适合纯推送)

五、Vue3 实战封装(生产可用)

六、React 实战封装

七、高级优化策略(必看)

7.1 防止中文乱码

7.2 渲染节流(防止卡顿)

7.3 可取消流(避免内存泄漏)

7.4 XSS 防护

八、高频问题与排查

8.1 常见问题

8.2 调试方法

九、总结


一、流式输出核心原理

1.1 什么是流式输出?

流式输出:服务端使用 HTTP 分块传输编码(Chunked Transfer Encoding),将数据拆分为多个块(chunk)持续推送;前端边接收边解析边渲染,不等待完整响应。

对比传统请求:

  • 传统:等待全部数据返回 → 一次性渲染
  • 流式:来一块渲染一块 → 连续展示

1.2 核心优势

  • 首屏延迟极低
  • 内存占用更小(不缓存全量数据)
  • 用户体验更流畅(打字机、实时日志)
  • 适合超长文本、AI 回复、数据流推送

1.3 关键技术支撑

  • HTTP/1.1 Transfer-Encoding: chunked
  • 浏览器 Streams APIReadableStream
  • Fetch + TextDecoder
  • SSE(Server-Sent Events)
  • WebSocket(双向流场景)

二、三种主流方案对比

方案 优点 缺点 适用场景
Fetch + ReadableStream 最灵活、支持 POST / 请求头、可取消 代码稍多 AI 对话、自定义流、需要权限头
SSE(EventSource) 极简、自动重连、原生支持 只支持 GET、无法自定义头 日志推送、通知、简单单向流
WebSocket 全双工、极强控制力 重、需服务端改造 聊天、高实时双向通信

主流首选:AI 打字机 → Fetch Stream简单推送SSE


三、原生 JavaScript 实现(最通用)

3.1 Fetch + ReadableStream 标准实现

async function startFetchStream(url, onChunk, onFinish) {
  const controller = new AbortController()

  try {
    const response = await fetch(url, {
      method: 'POST',
      signal: controller.signal,
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ message: 'hello' })
    })

    if (!response.body) throw new Error('不支持流式')

    const reader = response.body.getReader()
    const decoder = new TextDecoder('utf-8', { stream: true })

    while (true) {
      const { done, value } = await reader.read()
      if (done) break

      const chunk = decoder.decode(value)
      onChunk?.(chunk)
    }

    onFinish?.()
  } catch (err) {
    console.error('流异常:', err)
  }

  return controller
}

3.2 使用示例

const output = document.getElementById('output')

startFetchStream(
  '/api/stream',
  (chunk) => {
    output.innerHTML += chunk
    output.scrollTop = output.scrollHeight
  },
  () => console.log('流结束')
)

四、SSE 极简实现(适合纯推送)

function startSSE(url, onMessage) {
  const es = new EventSource(url)

  es.onmessage = (e) => {
    if (e.data === '[DONE]') {
      es.close()
      return
    }
    onMessage?.(e.data)
  }

  es.onerror = () => es.close()
  return es
}

使用:

startSSE('/api/sse', (chunk) => {
  output.innerHTML += chunk
})

五、Vue3 实战封装(生产可用)

<!-- StreamOutput.vue -->
<template>
  <div class="stream-box" ref="box">{{ content }}</div>
</template>

<script setup>
import { ref, onUnmounted } from 'vue'

const content = ref('')
const box = ref(null)
let controller = null

async function startStream() {
  content.value = ''

  controller = await startFetchStream(
    '/api/chat',
    (chunk) => {
      content.value += chunk
      box.value.scrollTop = box.value.scrollHeight
    }
  )
}

onUnmounted(() => {
  controller?.abort()
})
</script>

六、React 实战封装

import { useState, useEffect, useRef } from 'react'

export function StreamComponent() {
  const [text, setText] = useState('')
  const ref = useRef(null)
  const controllerRef = useRef(null)

  const start = async () => {
    setText('')
    controllerRef.current = startFetchStream('/api/stream', (chunk) => {
      setText(prev => prev + chunk)
      ref.current.scrollTop = ref.current.scrollHeight
    })
  }

  useEffect(() => {
    return () => controllerRef.current?.abort()
  }, [])

  return <div ref={ref} className="stream">{text}</div>
}

七、高级优化策略(必看)

7.1 防止中文乱码

const decoder = new TextDecoder('utf-8', { stream: true })

stream: true 允许跨块拼接字符,避免中文截断乱码。

7.2 渲染节流(防止卡顿)

let buffer = []
let timer = null

function pushChunk(chunk) {
  buffer.push(chunk)
  if (!timer) {
    timer = setTimeout(() => {
      onChunk(buffer.join(''))
      buffer = []
      timer = null
    }, 30)
  }
}

7.3 可取消流(避免内存泄漏)

const controller = new AbortController()
fetch(url, { signal: controller.signal })

// 取消
controller.abort()

7.4 XSS 防护

function escapeHtml(s) {
  return s.replace(/[&<>"']/g, c => ({ '&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;' }[c]))
}

八、高频问题与排查

8.1 常见问题

  1. 流不生效:服务端未返回 Transfer-Encoding: chunked
  2. 中文乱码:缺少 { stream: true }
  3. 无法跨域:服务端缺少 CORS 头
  4. 浏览器卡住:未做渲染节流
  5. 内存上涨:未取消流、未清理定时器

8.2 调试方法

  • Chrome F12 → Network → Response 查看分块
  • 确保服务端不断开连接
  • 使用 curl -N url 测试流是否正常

九、总结

前端流式输出的核心三要素:

  1. 协议:HTTP Chunked
  2. API:ReadableStream + TextDecoder
  3. 渲染:逐块追加、自动滚动、节流优化

最佳实践选型:

  • AI 对话 / 富交互流 → Fetch Stream
  • 日志 / 通知推送 → SSE
  • 双向高强度通信 → WebSocket

掌握本文内容,可轻松实现 ChatGPT 式打字机、实时日志、大屏数据流、大文件解析等所有前端流式场景。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐