最近在面试,在整理之前做过的项目,整理的过程中我会整理相关技术栈的实现,本篇是任何对话式ai应用都会遇到的流式输出协议的其中之一SSE,chatpgt用的就是这个,它是单向的,服务端向客户端推送
我用langChain做了一个案例,大家可以clone下来看下具体的实现,不过大多数的ai应用中还是会选择socket的,因为需要双向通信,但是SSE相比socket而言没那么重,写简单项目还是很方便的,比如智能对话助手什么的

项目代码Learn-LLM
📡 SSE 核心原理
1.1 什么是 SSE?
Server-Sent Events (SSE) 是 HTML5 标准的一项服务端推送技术,允许服务器主动向客户端推送数据。
核心特点
✅ 单向通信 - 服务器 → 客户端(推送)
✅ 基于 HTTP - 无需额外协议支持
✅ 持久连接 - 长连接,持续推送
✅ 文本格式 - 简单的文本协议
✅ 自动重连 - 浏览器内置重连机制
✅ 事件驱动 - 支持自定义事件类型
技术优势
特性 | SSE | WebSocket | 长轮询 |
---|---|---|---|
通信方向 | 单向(服务器→客户端) | 双向 | 单向 |
协议 | HTTP | WebSocket | HTTP |
自动重连 | ✅ 内置 | ❌ 需手动实现 | ❌ 需手动实现 |
复杂度 | 简单 | 中等 | 简单 |
适用场景 | 推送通知、流式输出 | 实时聊天、游戏 | 简单轮询 |
🔧 SSE 实现详解
2.1 前端实现(EventSource API)
typescript
// 1. 创建 SSE 连接
const eventSource = new EventSource('/api/streaming/sse?message=hello');
// 2. 监听默认消息
eventSource.onmessage = (event) => {
const data = JSON.parse(event.data);
console.log('收到数据:', data);
};
// 3. 监听自定义事件
eventSource.addEventListener('status', (event) => {
console.log('状态更新:', event.data);
});
eventSource.addEventListener('progress', (event) => {
console.log('进度更新:', event.data);
});
eventSource.addEventListener('complete', (event) => {
console.log('完成:', event.data);
eventSource.close(); // 关闭连接
});
// 4. 连接生命周期
eventSource.onopen = () => {
console.log('✅ SSE 连接已建立');
};
eventSource.onerror = (error) => {
console.error('❌ SSE 错误:', error);
// 浏览器会自动尝试重连
};
// 5. 手动关闭连接
eventSource.close();
EventSource 状态
typescript
EventSource.CONNECTING(0); // 连接中
EventSource.OPEN(1); // 已连接
EventSource.CLOSED(2); // 已关闭
// 检查状态
if (eventSource.readyState === EventSource.OPEN) {
console.log('连接正常');
}
2.2 后端实现(Next.js)
typescript
import { NextRequest } from 'next/server';
export async function GET(request: NextRequest) {
const encoder = new TextEncoder();
const readable = new ReadableStream({
async start(controller) {
try {
// 发送基本消息
controller.enqueue(
encoder.encode('data: {"content": "Hello World"}\n\n')
);
// 发送自定义事件
controller.enqueue(encoder.encode('event: status\ndata: 处理中\n\n'));
// 发送进度事件
controller.enqueue(encoder.encode('event: progress\ndata: 50%\n\n'));
// 发送完成事件
controller.enqueue(
encoder.encode('event: complete\ndata: 处理完成\n\n')
);
// 可选:主动关闭
// controller.close();
} catch (error) {
controller.error(error);
}
},
cancel(reason) {
console.log('客户端关闭连接:', reason);
},
});
return new Response(readable, {
headers: {
'Content-Type': 'text/event-stream', // ← SSE 的核心标识
'Cache-Control': 'no-cache',
Connection: 'keep-alive',
'Access-Control-Allow-Origin': '*',
},
});
}
2.3 SSE 数据格式规范
yaml
# 基本消息格式
data: Hello World\n\n
# 多行数据
data: 第一行\n
data: 第二行\n\n
# 自定义事件
event: notification\n
data: 新消息\n\n
# 带 ID 的消息(用于断线重连)
id: 123\n
data: 可重连的消息\n\n
# 设置重连时间(毫秒)
retry: 3000\n\n
# 重要规则:
# 1. 每个消息必须以两个换行符结尾 \n\n
# 2. data 字段可以多行
# 3. event 字段定义自定义事件类型
# 4. id 字段用于断线重连时的位置恢复
2.4 ChatGPT 流式对话实现
typescript
export async function GET(request: NextRequest) {
const searchParams = request.nextUrl.searchParams;
const userMessage = searchParams.get('message') || 'Hello';
const encoder = new TextEncoder();
const readable = new ReadableStream({
async start(controller) {
// 初始化 LangChain ChatOpenAI
const llm = new ChatOpenAI({
openAIApiKey: process.env.OPEN_API_KEY,
modelName: 'gpt-3.5-turbo',
temperature: 0.7,
streaming: true, // ← 开启流式输出
});
// 创建处理链
const chain = chatPrompt.pipe(llm).pipe(new StringOutputParser());
// 开始流式调用
const stream = await chain.stream({ userMessage });
// 逐块发送
for await (const chunk of stream) {
controller.enqueue(
encoder.encode(`data: ${JSON.stringify({ content: chunk })}\n\n`)
);
}
// 发送完成事件
controller.enqueue(
encoder.encode('event: complete\ndata: AI对话完成\n\n')
);
},
});
return new Response(readable, {
headers: {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
Connection: 'keep-alive',
},
});
}
🏗️ 技术栈解析
vbnet
┌─────────────────────────────────────────┐
│ SSE (Server-Sent Events) │ ← 应用层协议(HTML5 标准)
├─────────────────────────────────────────┤
│ 前端: EventSource API │ ← SSE 专属 API
│ 格式: data:, event:, id:, retry: │ ← SSE 专属格式
│ 头部: text/event-stream │ ← SSE 专属标识
└─────────────────────────────────────────┘
↓ 使用(但不专属)
┌─────────────────────────────────────────┐
│ Web Streams API (通用) │ ← 通用流处理 API
│ - ReadableStream │
│ - WritableStream │
│ - TransformStream │
│ - Controller (enqueue, close, error) │
└─────────────────────────────────────────┘
↓ 使用
┌─────────────────────────────────────────┐
│ Web Encoding API (通用) │ ← 通用编码 API
│ - TextEncoder (字符串 → 字节) │
│ - TextDecoder (字节 → 字符串) │
└─────────────────────────────────────────┘
↓ 基于
┌─────────────────────────────────────────┐
│ HTTP 协议 │ ← 传输层协议
└─────────────────────────────────────────┘
3.2 核心组件归属
技术 | 归属 | 用途 | 是否 SSE 专属 |
---|---|---|---|
EventSource |
SSE API | 接收 SSE 消息 | ✅ SSE 专属 |
text/event-stream |
SSE 标准 | 标识 SSE 响应 | ✅ SSE 专属 |
data:\n\n 格式 |
SSE 协议 | SSE 消息格式 | ✅ SSE 专属 |
TextEncoder |
Web Encoding API | 字符串转字节 | ❌ 通用工具 |
ReadableStream |
Streams API | 流式数据处理 | ❌ 通用工具 |
controller |
Streams API | 控制流的发送 | ❌ 通用工具 |
3.3 为什么 SSE 需要这些 API?
🤔 核心问题:HTTP Response 只能返回字节流
typescript
// ❌ HTTP 响应不能直接发送 JavaScript 字符串
return new Response('Hello World'); // 这会被自动转换为字节
// ✅ HTTP 响应必须是字节流
return new Response(ReadableStream<Uint8Array>); // 字节流
💡 SSE 的数据流转过程
markdown
1. JavaScript 字符串
↓ TextEncoder(字符串 → 字节)
2. Uint8Array 字节数组
↓ ReadableStream(组织成流)
3. ReadableStream<Uint8Array>
↓ HTTP Response
4. 浏览器接收字节流
↓ EventSource(解析 SSE 格式)
5. JavaScript 字符串事件
📝 为什么需要 Web Encoding API?
问题:HTTP 只传输字节,JavaScript 操作字符串
typescript
// SSE 格式是字符串
const sseMessage = 'data: Hello World\n\n';
// ❌ 不能直接发送字符串
controller.enqueue(sseMessage); // 类型错误!
// ✅ 必须先转换为字节
const encoder = new TextEncoder();
controller.enqueue(encoder.encode(sseMessage)); // Uint8Array
TextEncoder 的作用:
- 将 JavaScript 字符串编码为 UTF-8 字节数组
- 处理多语言字符(中文、emoji 等)
- 确保网络传输的正确性
typescript
const encoder = new TextEncoder();
// 示例:编码不同内容
encoder.encode('Hello'); // Uint8Array [72, 101, 108, 108, 111]
encoder.encode('你好'); // Uint8Array [228, 189, 160, 229, 165, 189]
encoder.encode('🎉'); // Uint8Array [240, 159, 142, 137]
🌊 为什么需要 Web Streams API?
问题 1:SSE 需要持续推送数据
typescript
// ❌ 普通响应只能一次性返回
return new Response('data: message\n\n'); // 发送后就结束了
// ✅ ReadableStream 可以持续推送
const readable = new ReadableStream({
async start(controller) {
// 可以多次发送
controller.enqueue(encoder.encode('data: message1\n\n'));
await sleep(1000);
controller.enqueue(encoder.encode('data: message2\n\n'));
await sleep(1000);
controller.enqueue(encoder.encode('data: message3\n\n'));
// 连接保持打开...
},
});
问题 2:SSE 需要控制发送时机
typescript
// ReadableStream 的 controller 提供精确控制
const readable = new ReadableStream({
async start(controller) {
// 1. 立即发送初始消息
controller.enqueue(encoder.encode('data: 开始\n\n'));
// 2. 等待异步操作
const result = await fetchData();
// 3. 根据结果发送
for await (const chunk of result) {
controller.enqueue(encoder.encode(`data: ${chunk}\n\n`));
}
// 4. 发送完成事件
controller.enqueue(encoder.encode('event: complete\ndata: 结束\n\n'));
// 5. 可选:关闭流
// controller.close();
},
});
问题 3:SSE 需要处理客户端断开
typescript
const readable = new ReadableStream({
async start(controller) {
const interval = setInterval(() => {
controller.enqueue(encoder.encode('data: tick\n\n'));
}, 1000);
// ✅ 关键:客户端断开时清理资源
return () => {
clearInterval(interval);
console.log('客户端断开,已清理定时器');
};
},
cancel(reason) {
// 客户端主动关闭时触发
console.log('客户端取消连接:', reason);
},
});
🔄 完整的 SSE 数据流
typescript
export async function GET(request: NextRequest) {
const encoder = new TextEncoder(); // ← 步骤1: 准备编码器
const readable = new ReadableStream({
// ← 步骤2: 创建流
async start(controller) {
// 步骤3: 准备字符串数据
const message = 'Hello World';
const sseFormat = `data: ${message}\n\n`;
// 步骤4: 字符串 → 字节
const bytes = encoder.encode(sseFormat);
// 步骤5: 推送到流中
controller.enqueue(bytes);
// 步骤6: 可以继续推送...
// controller.enqueue(encoder.encode('data: more\n\n'));
},
});
// 步骤7: 包装为 HTTP 响应
return new Response(readable, {
headers: {
'Content-Type': 'text/event-stream', // ← 告诉浏览器这是 SSE
},
});
}
// 客户端接收:
// ReadableStream<Uint8Array> → EventSource 解析 → JavaScript 事件
📊 对比:有无这些 API 的区别
场景 | 没有这些 API | 使用这些 API |
---|---|---|
发送数据 | ❌ 只能一次性返回 | ✅ 可以持续推送 |
字符串处理 | ❌ 需要手动处理编码 | ✅ TextEncoder 自动处理 |
流控制 | ❌ 无法控制发送时机 | ✅ controller 精确控制 |
资源清理 | ❌ 连接断开无通知 | ✅ cancel() 回调清理 |
背压处理 | ❌ 无法知道客户端状态 | ✅ desiredSize 指示队列 |
错误处理 | ❌ 只能返回错误响应 | ✅ 可以在流中发送错误 |
🌊 Web Streams API 完整介绍
4.1 三大核心流类型
ReadableStream(可读流)📖
typescript
const readable = new ReadableStream({
// 初始化
start(controller) {
controller.enqueue('数据块1');
controller.enqueue('数据块2');
controller.close();
},
// 消费者准备好接收更多数据时调用
pull(controller) {
// 可以延迟发送数据
},
// 消费者取消订阅时调用
cancel(reason) {
console.log('取消原因:', reason);
},
});
// 消费方式1: Reader
const reader = readable.getReader();
const { value, done } = await reader.read();
// 消费方式2: for await...of(推荐)
for await (const chunk of readable) {
console.log(chunk);
}
应用场景:
- ✅ SSE 服务器推送
- ✅ Fetch API 响应体
- ✅ 文件读取
- ✅ WebSocket 数据接收
WritableStream(可写流)✍️
typescript
const writable = new WritableStream({
start(controller) {
console.log('写入流初始化');
},
async write(chunk, controller) {
console.log('接收到:', chunk);
await saveToDatabase(chunk);
},
close() {
console.log('写入完成');
},
abort(reason) {
console.log('写入中止:', reason);
},
});
// 使用方式
const writer = writable.getWriter();
await writer.write('Hello');
await writer.write('World');
await writer.close();
应用场景:
- 文件下载保存
- 数据持久化
- 网络上传
TransformStream(转换流)🔄
typescript
// 创建转换流:将文本转为大写
const uppercase = new TransformStream({
transform(chunk, controller) {
controller.enqueue(chunk.toUpperCase());
},
});
// 使用管道连接
readable.pipeThrough(uppercase).pipeTo(writable);
// 实际例子:压缩流
const compressor = new TransformStream({
async transform(chunk, controller) {
const compressed = await compress(chunk);
controller.enqueue(compressed);
},
flush(controller) {
// 处理最后的数据
},
});
// 链式处理
fetch('/api/data')
.then((response) => response.body)
.pipeThrough(decompressor)
.pipeThrough(jsonParser)
.pipeTo(display);
应用场景:
- 数据压缩/解压
- 编码转换(Base64、UTF-8)
- 数据加密/解密
- 文本处理(过滤、替换)
4.2 核心控制器
ReadableStreamDefaultController
typescript
interface ReadableStreamDefaultController {
enqueue(chunk: any): void; // 添加数据块到队列
close(): void; // 关闭流
error(error: any): void; // 发送错误
readonly desiredSize: number; // 队列中可容纳的数据大小(背压)
}
// 使用示例
const readable = new ReadableStream({
start(controller) {
// 检查背压
if (controller.desiredSize <= 0) {
console.log('队列已满,暂停发送');
return;
}
controller.enqueue('数据');
// 发生错误
if (error) {
controller.error(new Error('处理失败'));
}
// 完成
controller.close();
},
});
WritableStreamDefaultController
typescript
interface WritableStreamDefaultController {
error(error: any): void; // 发送错误信号
}
TransformStreamDefaultController
typescript
interface TransformStreamDefaultController {
enqueue(chunk: any): void; // 输出转换后的数据
terminate(): void; // 终止流
readonly desiredSize: number; // 背压指示
}
4.3 读取器与写入器
ReadableStreamDefaultReader
typescript
const reader = readable.getReader();
// 读取数据
while (true) {
const { value, done } = await reader.read();
if (done) break;
console.log('读取到:', value);
}
// 取消读取
await reader.cancel('用户取消');
// 释放锁(允许其他 reader)
reader.releaseLock();
WritableStreamDefaultWriter
typescript
const writer = writable.getWriter();
// 写入数据
await writer.write('数据块1');
await writer.write('数据块2');
// 关闭
await writer.close();
// 中止
await writer.abort('发生错误');
// 释放锁
writer.releaseLock();
4.4 管道操作
typescript
// 1. pipeTo - 流向可写流
await readable.pipeTo(writable);
// 2. pipeThrough - 流经转换流
const transformed = readable
.pipeThrough(transformStream1)
.pipeThrough(transformStream2);
// 3. 实际应用:文件处理流水线
fetch('/api/large-file')
.then((response) => response.body) // ReadableStream
.pipeThrough(decompressStream) // 解压
.pipeThrough(decryptStream) // 解密
.pipeThrough(parseStream) // 解析
.pipeThrough(validateStream) // 验证
.pipeTo(saveToFileStream); // 保存
// 4. 带选项的管道
await readable.pipeTo(writable, {
preventClose: true, // 不自动关闭 writable
preventAbort: true, // 不传播 abort
preventCancel: true, // 不传播 cancel
signal: abortSignal, // 取消信号
});
4.5 完整 API 对照表
API | 类型 | 主要方法/属性 | 用途 |
---|---|---|---|
ReadableStream | 可读流 | getReader() , pipeThrough() , pipeTo() |
读取数据源 |
WritableStream | 可写流 | getWriter() , abort() |
写入数据 |
TransformStream | 转换流 | readable , writable |
数据转换 |
ReadableStreamDefaultController | 控制器 | enqueue() , close() , error() |
控制可读流 |
WritableStreamDefaultController | 控制器 | error() |
控制可写流 |
TransformStreamDefaultController | 控制器 | enqueue() , terminate() |
控制转换流 |
ReadableStreamDefaultReader | 读取器 | read() , cancel() , releaseLock() |
读取数据块 |
ReadableStreamBYOBReader | 字节读取器 | read(buffer) |
高效字节读取 |
WritableStreamDefaultWriter | 写入器 | write() , close() , abort() |
写入数据块 |
🔤 Web Encoding API 完整介绍
5.1 核心概念
Web Encoding API 提供字符串与字节之间的编码转换功能,是处理网络传输、文件操作的基础工具。
为什么需要编码?
javascript
问题:计算机只能处理数字(字节),网络也只能传输字节
解决:需要在 JavaScript 字符串 ↔ 字节数组 之间转换
JavaScript 字符串 "Hello"
↓ TextEncoder
Uint8Array [72, 101, 108, 108, 111] ← 可以通过网络传输
↓ TextDecoder
JavaScript 字符串 "Hello"
5.2 TextEncoder(编码器)
基本用法
typescript
// 创建编码器(默认 UTF-8)
const encoder = new TextEncoder();
// 字符串 → 字节数组
const bytes = encoder.encode('Hello World');
console.log(bytes); // Uint8Array(11) [72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100]
// 查看编码格式
console.log(encoder.encoding); // "utf-8"(只读,固定为 UTF-8)
API 详解
typescript
interface TextEncoder {
readonly encoding: string; // 固定为 "utf-8"
// 编码整个字符串
encode(input?: string): Uint8Array;
// 编码到已有缓冲区(流式编码)
encodeInto(
source: string,
destination: Uint8Array
): TextEncoderEncodeIntoResult;
}
interface TextEncoderEncodeIntoResult {
read: number; // 已读取的源字符数
written: number; // 已写入的字节数
}
encode() 方法
typescript
const encoder = new TextEncoder();
// 1. 基本使用
encoder.encode('Hello'); // Uint8Array [72, 101, 108, 108, 111]
// 2. 空字符串
encoder.encode(''); // Uint8Array []
// 3. 中文字符(UTF-8 编码,每个中文 3 字节)
encoder.encode('你好'); // Uint8Array [228, 189, 160, 229, 165, 189]
// 4. Emoji(UTF-8 编码,4 字节)
encoder.encode('🎉'); // Uint8Array [240, 159, 142, 137]
// 5. 混合内容
encoder.encode('Hello 你好 🎉'); // 包含所有字符的字节表示
encodeInto() 方法(流式编码)
typescript
const encoder = new TextEncoder();
// 创建目标缓冲区
const buffer = new Uint8Array(20);
// 编码到缓冲区
const result = encoder.encodeInto('Hello World', buffer);
console.log(result);
// { read: 11, written: 11 }
// read: 读取了 11 个字符
// written: 写入了 11 个字节
console.log(buffer);
// Uint8Array [72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0]
// H e l l o 空 W o r l d ← 后面是未使用的 0
// 缓冲区不足的情况
const smallBuffer = new Uint8Array(5);
const result2 = encoder.encodeInto('Hello World', smallBuffer);
console.log(result2);
// { read: 5, written: 5 } ← 只能写入 5 个字节
console.log(smallBuffer);
// Uint8Array [72, 101, 108, 108, 111] ← "Hello" 的字节
实际应用场景
typescript
// ✅ SSE 数据编码
const encoder = new TextEncoder();
controller.enqueue(encoder.encode('data: Hello\n\n'));
// ✅ WebSocket 发送文本
const message = encoder.encode(JSON.stringify({ type: 'chat', content: 'Hi' }));
websocket.send(message);
// ✅ 文件写入
const fileContent = encoder.encode('File content here');
await writeFile('output.txt', fileContent);
// ✅ 计算字符串字节长度
const text = 'Hello 你好';
const byteLength = encoder.encode(text).length;
console.log(byteLength); // 14 字节(5 + 1 + 6 + 2)
5.3 TextDecoder(解码器)
基本用法
typescript
// 创建解码器(默认 UTF-8)
const decoder = new TextDecoder();
// 字节数组 → 字符串
const bytes = new Uint8Array([72, 101, 108, 108, 111]);
const text = decoder.decode(bytes);
console.log(text); // "Hello"
// 查看解码格式
console.log(decoder.encoding); // "utf-8"
API 详解
typescript
interface TextDecoder {
readonly encoding: string; // 编码格式(如 "utf-8")
readonly fatal: boolean; // 是否抛出解码错误
readonly ignoreBOM: boolean; // 是否忽略字节序标记
// 解码字节数组
decode(
input?: BufferSource, // Uint8Array 或 ArrayBuffer
options?: TextDecodeOptions // 解码选项
): string;
}
interface TextDecodeOptions {
stream?: boolean; // 流式解码(保留不完整字符)
}
支持的编码格式
typescript
// UTF-8(默认,推荐)
const utf8Decoder = new TextDecoder('utf-8');
// 其他编码格式
const gbkDecoder = new TextDecoder('gbk'); // 中文 GBK
const latinDecoder = new TextDecoder('iso-8859-1'); // 拉丁文
const shiftJisDecoder = new TextDecoder('shift-jis'); // 日文
// 检查支持的编码
try {
new TextDecoder('unknown-encoding');
} catch (error) {
console.error('不支持的编码格式');
}
decode() 方法
typescript
const decoder = new TextDecoder();
// 1. 基本解码
const bytes1 = new Uint8Array([72, 101, 108, 108, 111]);
decoder.decode(bytes1); // "Hello"
// 2. 解码中文
const bytes2 = new Uint8Array([228, 189, 160, 229, 165, 189]);
decoder.decode(bytes2); // "你好"
// 3. 解码 Emoji
const bytes3 = new Uint8Array([240, 159, 142, 137]);
decoder.decode(bytes3); // "🎉"
// 4. 空字节
decoder.decode(new Uint8Array([])); // ""
// 5. 使用 ArrayBuffer
const buffer = new ArrayBuffer(5);
const view = new Uint8Array(buffer);
view.set([72, 101, 108, 108, 111]);
decoder.decode(buffer); // "Hello"
流式解码
typescript
const decoder = new TextDecoder();
// 问题:多字节字符可能被分割
const part1 = new Uint8Array([228, 189]); // "你" 的前 2 字节
const part2 = new Uint8Array([160]); // "你" 的最后 1 字节
// ❌ 不使用 stream 选项
decoder.decode(part1); // "�" (乱码,字符不完整)
decoder.decode(part2); // "�" (乱码)
// ✅ 使用 stream 选项
const streamDecoder = new TextDecoder();
const text1 = streamDecoder.decode(part1, { stream: true }); // ""(等待更多字节)
const text2 = streamDecoder.decode(part2, { stream: false }); // "你"(完成解码)
console.log(text1 + text2); // "你"
错误处理
typescript
// 1. fatal 模式(遇到无效字节抛出错误)
const fatalDecoder = new TextDecoder('utf-8', { fatal: true });
try {
const invalidBytes = new Uint8Array([0xff, 0xfe]); // 无效的 UTF-8
fatalDecoder.decode(invalidBytes);
} catch (error) {
console.error('解码失败:', error); // EncodingError
}
// 2. 非 fatal 模式(替换为 � )
const decoder = new TextDecoder('utf-8', { fatal: false });
const invalidBytes = new Uint8Array([0xff, 0xfe]);
decoder.decode(invalidBytes); // "��"(替换字符)
5.4 完整 API 对照表
API | 方法/属性 | 参数 | 返回值 | 用途 |
---|---|---|---|---|
TextEncoder | constructor() |
- | TextEncoder |
创建 UTF-8 编码器 |
encode(str) |
string |
Uint8Array |
字符串 → 字节数组 | |
encodeInto(str, buffer) |
string, Uint8Array |
{read, written} |
编码到已有缓冲区 | |
encoding |
- | "utf-8" |
编码格式(只读) | |
TextDecoder | constructor(label?, options?) |
string, {fatal?, ignoreBOM?} |
TextDecoder |
创建解码器 |
decode(buffer?, options?) |
BufferSource, {stream?} |
string |
字节数组 → 字符串 | |
encoding |
- | string |
编码格式(只读) | |
fatal |
- | boolean |
是否抛出错误(只读) | |
ignoreBOM |
- | boolean |
是否忽略 BOM(只读) |
5.5 总结
Web Encoding API 核心要点:
- TextEncoder:字符串 → 字节(仅支持 UTF-8)
- TextDecoder:字节 → 字符串(支持多种编码)
- 在 SSE 中的作用 :
- 将 SSE 格式字符串编码为字节
- 使 HTTP Response 能够传输文本数据
- 性能优化 :
- 复用编码器/解码器实例
- 大量编码时使用
encodeInto()
- 流式解码时使用
{ stream: true }