技术手记

TransformStream 完全指南:重新定义前端流式数据处理

2026年7月22日◷ 3 分钟阅读◉ 1 次浏览
TransformStream 完全指南:重新定义前端流式数据处理

一、为什么你需要 TransformStream?

传统的 EventSource 虽然简单,但有一个致命短板:只支持 GET 请求,无法自定义请求头

当你需要:
- 用 POST 发送对话历史给 AI
- 携带 Authorization Header 做鉴权
- 取消正在进行的 AI 响应
- 对数据流做解析、过滤、限流

EventSource 全都不行。这时候,fetch + TransformStream 就是标准答案。


二、TransformStream 核心概念

TransformStream 是浏览器原生的流式数据处理管道

ReadableStream (后端数据)
    → TextDecoderStream (二进制 → 字符串)
    → TransformStream (自定义解析)
    → 最终消费 (渲染到页面)

创建 TransformStream

const stream = new TransformStream({
  transform(chunk, controller) {
    // chunk: 每一段收到的数据
    // controller.enqueue(data): 把处理后的数据往下传
  },
  flush(controller) {
    // 流结束时执行(可选)
  }
});

三、实现 AI 打字机的终极方案

完整代码

async function startAIStream() {
  // 1. 发起 POST 请求,携带对话历史 + API Key
  const response = await fetch('/api/ai/chat', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': 'Bearer your-api-key'
    },
    body: JSON.stringify({
      messages: [{ role: 'user', content: '你好' }],
      stream: true
    })
  });

  // 2. 构建流式处理管道
  const reader = response.body
    // 二进制 → 字符串
    .pipeThrough(new TextDecoderStream())
    // 自定义解析 SSE 格式
    .pipeThrough(new TransformStream({
      transform(chunk, controller) {
        // SSE 格式: data: {"content": "你"}
        const lines = chunk.split('\n');

        for (const line of lines) {
          if (line.startsWith('data: ')) {
            const jsonStr = line.slice(6);
            if (jsonStr === '[DONE]') continue;

            try {
              const data = JSON.parse(jsonStr);
              const content = data.choices?.[0]?.delta?.content || '';
              if (content) controller.enqueue(content);
            } catch (e) {}
          }
        }
      }
    }))
    .getReader();

  // 3. 循环读取并渲染
  while (true) {
    const { done, value } = await reader.read();
    if (done) break;
    document.getElementById('app').textContent += value;
  }
}

startAIStream();

四、逐行精讲

1. pipeThrough(new TextDecoderStream())

把后端返回的二进制流转换成字符串流。必须传 { stream: true }(默认就是),表示持续解析而非一次性。

2. TransformStream 的 transform 方法

transform(chunk, controller) {
  // chunk:每次收到的文本片段
  // controller.enqueue(data):把处理后的数据往下传
}

这是整个管道的心脏。你要在这里做:
- 按行拆分 SSE 数据
- 过滤非 data: 开头的行
- 解析 JSON 提取 content
- 把有效内容 enqueue 到下游

3. AI 接口的固定 SSE 格式

后端返回的格式一定是:

data: {"choices":[{"delta":{"content":"你"}}]}
data: {"choices":[{"delta":{"content":"好"}}]}
data: [DONE]

所以前端必须按行解析 → 取 data: 开头 → 截取 JSON → 解析 content → 逐字渲染。


五、高级功能

取消 AI 响应

const controller = new AbortController();

fetch(url, { signal: controller.signal })
  .then(/* ... */);

// 用户点击停止按钮
controller.abort();

限流(防抖渲染)

let buffer = '';
let timer;

transform(chunk, controller) {
  buffer += chunk;

  clearTimeout(timer);
  timer = setTimeout(() => {
    controller.enqueue(buffer);
    buffer = '';
  }, 16); // 60fps 渲染帧率
}

流式 Markdown 解析

一边接收文字,一边解析 #**、代码块等 Markdown 语法。这是面试加分项——不截断标签的增量渲染。


六、EventSource vs TransformStream(面试必背)

特性 EventSource TransformStream
请求方式 仅 GET POST/GET 都支持
自定义请求头
流控制 极强(暂停/取消/限流)
解析能力 固定 完全自定义
适用场景 简单通知 AI 流式输出、复杂流处理
现代框架推荐 ✅ 首选

标准答案:AI 流式输出优先使用 fetch + TransformStream,因为支持 POST、自定义 Header、可取消、可精细控制流。


七、总结

  • TransformStream 是浏览器原生流处理 API
  • 用它构建的管道 = 接收流 → 解码 → 解析 → 渲染
  • EventSource 更强大:POST、自定义头、取消、限流
  • 核心公式:ReadableStream → TextDecoderStream → TransformStream → 渲染
  • AI 打字机的最优解就藏在这条管道里

下次面试官问"怎么做 AI 打字机?",你可以从容回答:"用 fetch + TextDecoderStream + TransformStream 解析 SSE 格式。

Comments 留言讨论

还没有评论,来抢个沙发,聊聊你的看法~

Michael.Meng

michaelnews@126.com
用 AI 记录,用文字沉淀

© 2026 Michael Meng · 保留所有权利 · Powered by FastAPI + Nuxt