在前端开发中,处理 非流式数据流式数据 的方式不同。根据是否完整接收数据、是否实时渲染的需求,可以分为以下四种典型场景:


一、四类常见场景总结

类型 数据完整性 是否实时渲染 适用技术/方法
A 完整数据(一次性返回) 否(等全部加载完) fetch, axios, JSON.parse()
B 完整数据(一次性返回) 是(模拟逐字显示) setTimeout / requestAnimationFrame 模拟打字效果
C 不完整数据(分段传输) 否(等全部加载完) buffer 缓存 + 最终解析
D 不完整数据(分段传输) 是(边接收边渲染) ReadableStream, SSE, WebSocket, JSON 增量解析

场景对比表

场景 是否流式 是否实时渲染 是否需要增量解析 代表应用
A 非流式 不需要 获取静态数据
B 非流式 不需要 AI 回答展示
C 流式 需要 日志聚合、大文件解析
D 流式 需要 AI 聊天机器人、代码生成器

具体情况和项目还需要具体分析,并非生搬这四种场景。以下为场景对比的详解与实现:

场景详解与实现方案


场景 A:完整数据 + 非实时渲染(最常见)

适用于传统 API 请求,如获取用户列表、文章内容等。

示例代码:
const response = await fetch('/api/data');
const data = await response.json();
render(data);
渲染逻辑:
function render(data) {
  document.getElementById('content').innerText = data.content;
}

场景 B:完整数据 + 实时渲染(模拟打字效果)

适用于需要“逐字显示”的视觉效果,比如 AI 对话界面。

示例代码:
const response = await fetch('/api/data');
const data = await response.json();

simulateTyping(data.content, document.getElementById('output'), 50);
打字函数实现:
function simulateTyping(text, element, interval = 50) {
  let index = 0;
  const timer = setInterval(() => {
    if (index < text.length) {
      element.textContent += text[index++];
    } else {
      clearInterval(timer);
    }
  }, interval);
}

场景 C:不完整数据 + 非实时渲染(等待拼接完成)

适用于大文件下载、日志聚合等场景,需等到所有数据接收完毕后再统一处理。

示例代码:
let buffer = '';

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

  buffer += decoder.decode(value, { stream: true });
}

// 等待完成后统一解析
const finalData = JSON.parse(buffer);
render(finalData);

场景 D:不完整数据 + 实时渲染(流式解析 + 边接收边展示)

适用于 AI 流式回复、聊天机器人、代码生成器 等场景,需边接收边解析边渲染。

核心流程:
  1. 接收 chunk 数据
  2. 拼接到 buffer
  3. 尝试增量解析
  4. 提取有效字段并更新 UI
示例代码(结合 jsonparse):
import sax from 'jsonparse';

let buffer = '';
const parser = new sax.Parser();

parser.onValue = function (value) {
  if (this.stack.length === 1 && this.key === 'content') {
    updateUI(value); // 实时渲染 content 字段
  }
};

async function processStream() {
  const response = await fetch('/api/stream-endpoint', {
    method: 'POST',
    body: JSON.stringify({ prompt: '讲个故事' })
  });

  const reader = response.body.getReader();
  const decoder = new TextDecoder();

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

    buffer += decoder.decode(value, { stream: true });

    try {
      parser.write(buffer);
      buffer = ''; // 成功解析后清空 buffer
    } catch (e) {
      // 忽略不完整 JSON 错误,继续等待更多数据
    }
  }

  parser.close();
}

function updateUI(text) {
  const container = document.getElementById('output');
  container.textContent += text;
}

技术选型建议

使用场景 推荐技术
非流式完整数据 fetch, axios, JSON.parse()
流式数据(HTTP SSE) EventSource, fetch + ReadableStream
WebSocket 数据流 WebSocket, Socket.IO
JSON 增量解析 jsonparse, clarinet
实时 UI 更新 requestAnimationFrame, DOM diff, 节流控制

Vue / React 场景优化建议

Vue 示例(使用 ref 控制 DOM)

<template>
  <div id="output">{{ outputText }}</div>
</template>

<script setup>
import { ref } from 'vue';
const outputText = ref('');

function updateUI(text) {
  outputText.value += text;
}
</script>

React 示例(使用 useStateuseRef

function ChatBox() {
  const [text, setText] = useState('');
  const containerRef = useRef(null);

  function updateUI(chunk) {
    setText(prev => prev + chunk);
    setTimeout(() => {
      containerRef.current.scrollTop = containerRef.current.scrollHeight;
    }, 0);
  }

  return (
    <div ref={containerRef} style={{ height: '300px', overflowY: 'auto' }}>
      {text}
    </div>
  );
}
Logo

火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。

更多推荐