当前位置:首页 > 文章列表 > 文章 > 前端 > 浏览器 MessageChannel 如何把端口交给 iframe

浏览器 MessageChannel 如何把端口交给 iframe

来源:17golang原创 2026-09-15 09:53:59 0浏览 收藏

主页面想把 MessageChannel 的端口交给 iframe,关键不是把 port2 当普通数据传过去,而是在 iframe.contentWindow.postMessage() 的第三个参数中转移它。父页面保留 port1,iframe 从初始 message 事件的 event.ports[0] 接管 port2,之后双方直接通过端口通信。

要点速览
  • 等 iframe 的 load 事件后再发送端口,避免接收端监听器尚未建立。
  • targetOrigin 要写成 iframe 的真实源;接收端还要检查 event.originevent.source
  • 端口转移后,发送方不再使用原来的 port2,业务消息统一从父页面的 port1 发出。

先分清两次消息:交付端口和传业务

这里其实有两条不同的链路。第一次是父页面调用 contentWindow.postMessage("init", targetOrigin, [port2]),它把端口所有权交给 iframe;第二次开始,父页面调用 port1.postMessage(data),iframe 调用接管后的端口发送回执。postMessage 的 transfer 数组只负责转移所有权,端口必须同时出现在接收事件的 event.ports 中。

阶段发送方接收位置要确认的点
初始化父页面 Windowiframe 的 Window messageevent.ports[0] 存在
业务通信父页面 port1iframe port2 的 message收到业务数据并能回 ack
回执iframe port2父页面 port1 的 message回执内容和请求对应

父页面:在 iframe 加载后转移 port2

父页面只保留 port1。示例用一个固定的 FRAME_ORIGIN,生产代码不要为了省事使用 *;如果 iframe 的协议、域名或端口变化,应当显式更新这个值。

const iframe = document.querySelector("#worker-frame");
const channel = new MessageChannel();
const FRAME_ORIGIN = "https://widget.example.com";

// 父页面只监听并使用 port1,port2 会在初始化时转移给 iframe。
channel.port1.onmessage = (event) => {
  if (event.data?.type === "widget-ready") {
    document.querySelector("#status").textContent = "iframe 已接通";
  }
};

// 等 iframe 完成加载,避免它错过接收初始化端口的 message 事件。
iframe.addEventListener("load", () => {
  iframe.contentWindow.postMessage(
    { type: "channel-init" },
    FRAME_ORIGIN,
    [channel.port2]
  );
}, { once: true });

function sendToFrame(payload) {
  // 端口已经转移后,业务消息从父页面保留的 port1 发出。
  channel.port1.postMessage({ type: "request", payload });
}
浏览器 MessageChannel 父页面保留 port1 并通过 postMessage transfer 转移 port2 给 iframe 的结构示意图
图1:父页面端口分工与 iframe 初始化交付的结构示意图,不是实际运行截图。

iframe:校验来源后接管 event.ports[0]

iframe 首先接收的是 Window 的 message,不是端口的业务消息。接收端要先验证来源,再确认消息类型和端口数量,最后将 event.ports[0] 保存下来。使用 onmessage 时浏览器会自动开始派发队列;如果改成 addEventListener,再补充调用 port.start() 更清晰。

const PARENT_ORIGIN = "https://portal.example.com";
let parentPort;

window.addEventListener("message", (event) => {
  // 先拒绝未知窗口和未知来源,避免把端口交给不可信页面。
  if (event.origin !== PARENT_ORIGIN || event.source !== window.parent) {
    return;
  }
  if (event.data?.type !== "channel-init" || event.ports.length !== 1) {
    return;
  }

  parentPort = event.ports[0];
  parentPort.onmessage = (messageEvent) => {
    if (messageEvent.data?.type !== "request") return;
    // 这里处理业务数据,并用同一端口回传接通确认。
    parentPort.postMessage({ type: "widget-ready", received: true });
  };
});
iframe 校验 event.origin 与 event.source 后从 event.ports[0] 接管 MessagePort 并回传确认的结构示意图
图2:iframe 端从初始 Window 消息接管端口,再通过端口回传结果的结构示意图,不是实际运行截图。

端口交不过去时,按这张清单排查

最常见的问题不是 API 不支持,而是链路中的对象和时机搞混。先看初始化消息是否抵达,再看端口是否存在,最后才检查业务消息。

现象优先检查修复方向
iframe 没有收到 messageload 时机与 targetOrigin等加载完成,并核对协议、主机、端口
event.ports 为空是否把 port2 放入第三参数使用 [channel.port2],不要只把它写进普通数据
初始化后父页面继续用 port2所有权已转移父页面只用 port1,iframe 只用接管的端口
业务消息没有回执iframe 是否绑定端口 onmessage保存 event.ports[0] 后再发送业务消息

常见问题

为什么不能直接访问跨域 iframe 的 document?

同源策略限制了跨源 DOM 访问。postMessage 提供的是受控的跨窗口通信入口,不能把它理解成绕过所有安全检查。

端口转移后还能在父页面调用 port2 吗?

不能把转移后的 port2 当作父页面可继续使用的端口。发送方应保留 port1,接收方使用收到的端口。

什么时候需要调用 port.start()?

使用 onmessage 时启动动作是隐含的;如果用 addEventListener("message", handler),可显式调用 start() 表明开始消费消息队列。

这套写法的判断标准很简单:初始化 Window 消息有一个端口,iframe 能从 event.ports[0] 接管它,之后双方都只使用各自的一端,并且来源校验和回执都能对上。

版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
Linux mount namespace 绑定挂载为何对宿主不可见Linux mount namespace 绑定挂载为何对宿主不可见
上一篇
Linux mount namespace 绑定挂载为何对宿主不可见
Go time.AfterFunc Reset 失败时如何判断旧回调状态
下一篇
Go time.AfterFunc Reset 失败时如何判断旧回调状态
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    543次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    516次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    500次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    485次学习
查看更多
AI推荐
  • PubMedQA数据集详解:生物医学问答基准、功能与应用指南
    PubMedQA
    深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
    31次使用
  • H2O EvalGPT:开源LLM大模型评估与排行榜工具
    H2O EvalGPT
    H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
    133次使用
  • LMArena是什么?伯克利AI模型评估平台使用指南与功能解析
    LMArena
    LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
    68次使用
  • 斯坦福HELM:大语言模型Holistic Evaluation整体评估框架详解
    HELM
    深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
    25次使用
  • CMMLU中文大模型评估基准:功能、使用教程与应用场景解析
    CMMLU
    深入了解CMMLU中文评估基准,涵盖67个学科主题,提供数据集下载、Zero-shot/Five-shot评估方法及排行榜,助力优化中文语言模型性能。
    16次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议隐私政策
返回登录
  • 重置密码