跨域 postmessage 无法传递迭代流对象,因其不可被结构化克隆算法序列化;应传递可序列化的指令、元数据或分块数据,并由接收端本地重建流。

跨域窗口间传递带有迭代流属性(如 ReadableStream、TransformStream 或自定义可迭代对象)的对象时,反序列化后属性丢失,根本原因不是数据“传错了”,而是这些对象本身**无法被结构化克隆算法(Structured Clone Algorithm)识别和复制**——它们属于不可序列化的宿主对象,浏览器在 postMessage() 过程中会直接忽略或静默丢弃。
明确哪些迭代流属性必然丢失
以下类型在跨域 postMessage 中一律无法保留:
-
ReadableStream/WritableStream/TransformStream实例(即使为空) - 含
[Symbol.asyncIterator]的自定义可迭代对象 - 使用
async function* () {}定义的异步生成器对象 - 任何绑定了底层资源(如网络连接、文件句柄、音频上下文)的流式封装对象
用可传输数据替代流对象本身
不要尝试“传流”,而应传递流所承载的**意图、元信息与分块数据载体**:
- 发送端将流操作拆解为可序列化指令:例如
{ type: 'FETCH_CHUNKED', url: '/api/data', format: 'json-lines' } - 对小规模流内容,提前读取并转为
ArrayBuffer或Uint8Array,再通过transferable选项传递(需同源或显式允许跨域 transfer) - 若必须复现流行为,只传配置参数(如分页游标、编码格式、chunk size),由接收端本地新建对应流
接收端重建流逻辑,而非还原对象
接收窗口不依赖原始对象结构,而是根据 payload 触发本地等效流程:
- 收到
{ type: 'START_STREAM', id: 'log-20260522', mode: 'text' }后,调用new ReadableStream(...)构造新流 - 配合
MessageChannel建立双向通信通道,用port.postMessage()分批推送解码后的 chunk 数据(字符串、数字、普通对象) - 对需要状态同步的场景(如暂停/恢复),单独发送控制消息:
{ type: 'STREAM_PAUSE', id: 'log-20260522' }
避免踩坑的硬性提醒
以下做法无效或危险,应杜绝:
- 对流对象调用
JSON.stringify()或structuredClone()—— 返回空对象或抛错 - 试图用
eval()或Function()动态重建流构造逻辑 —— 执行上下文隔离,且存在严重 XSS 风险 - 把
stream.getReader()结果传出去 —— reader 是强绑定原流的不可转移对象 - 依赖
Object.getOwnPropertyDescriptors()提取描述符再重建 —— 流的内部槽位(internal slots)完全不可访问、不可模拟











