
多年前我因为项目中频繁遇到 iframe 跨域通信问题写了一个小工具来封装postMessage。后来这个工具逐渐完善最终开源成了现在的iframe-js。这是我第一次认真写文章介绍这个项目。过去三年里它从最初只能发送消息逐步增加了 ACK 确认、握手协议、离线消息队列、RPC 远程调用、状态同步和 iframe 高度自适应等能力。这篇文章不想单纯介绍一个 npm 包而是想分享我在解决 iframe 通信问题时遇到的痛点以及iframe-js是如何一步步演变出来的。如果你也在处理跨域 iframe 通信希望它能帮你少踩一些坑。在前端项目中iframe 经常被用于嵌入第三方页面微前端子应用支付、登录、客服等独立模块Vue、React 项目之间的页面通信最常见的通信方式是原生window.postMessage。但随着业务变复杂通常会遇到几个问题消息发送时iframe 还没有加载完成导致消息丢失发送消息后不知道对方是否真正收到事件监听和回调越来越多代码容易变成“回调地狱”跨域 iframe 高度无法自动适应多个 iframe 同时存在时消息容易互相干扰缺少 Origin 白名单校验存在安全隐患。于是我开源了一个轻量的 iframe 跨域通信库iframe-js。项目地址npmhttps://www.npmjs.com/package/iframe-jsGitHubGitHub - 1503963513/iFramejs: 解决 IFrame 通信优雅使用 PostMessage · GitHub在线 DemoiFrame.js - Parent Page Demo安装npm install iframe-js也可以使用 pnpm 或 yarnpnpm add iframe-js基础用法父页面import Iframe from iframe-js; const iframe new Iframe({ container: document.querySelector(#child-frame), url: https://child.example.com/index.html, whiteList: [https://child.example.com], timeout: 5000 }); // 监听子页面事件 iframe.action(childReady, (event) { console.log(子页面已准备好:, event.data); }); // 向子页面发送事件 iframe.emit(hello, { message: 你好子页面 });页面中准备一个 iframe 容器iframe idchild-frame/iframe子页面import Iframe from iframe-js; const childApp new Iframe(child-frame); childApp.addWhiteList(https://parent.example.com); // 监听父页面发送的事件 childApp.action(hello, (event) { console.log(收到父页面消息:, event.data); childApp.emit(childReady, { status: success }); });Promise ACK 确认机制原生postMessage发送后默认无法知道消息是否被对方接收。iframe-js提供了 Promise 风格的 ACK APIconst success await iframe.emitToChildWithAck( requestPayment, { amount: 100 }, 8000 ); if (success) { console.log(子页面已确认收到消息); } else { console.log(发送超时或目标页面未响应); }如果 iframe 尚未加载完成消息会暂存在内部队列中等握手成功后自动发送。RPC 远程调用除了发送事件还可以像调用本地函数一样调用 iframe 中的远程函数。父页面暴露方法iframe.expose(getUserInfo, async ({ id }) { const response await fetch(/api/user/${id}); return response.json(); });子页面调用const userInfo await childApp.callRemote( getUserInfo, { id: 1001 }, 5000 ); console.log(userInfo);这对于微前端、嵌入式业务模块和跨页面服务调用非常方便。状态同步父页面可以向子页面同步全局状态iframe.setState({ theme: dark, language: zh-CN }); iframe.setState({ theme: light });子页面监听状态变化childApp.onStateChange((newState, oldState) { console.log(状态发生变化:, newState); });同一同步帧内多次调用setState会自动合并减少跨域通信次数。iframe 高度自动适应跨域 iframe 的高度自适应一直比较麻烦iframe-js内置了自动高度同步能力。父页面iframe.enableAutoResize();子页面childApp.startAutoResizer({ target: body, offset: 20 });当子页面内容发生变化时父页面中的 iframe 高度会自动调整可以避免出现双滚动条。安全白名单默认情况下建议明确配置允许通信的 Originconst iframe new Iframe({ container: document.querySelector(#child-frame), url: https://child.example.com/index.html, whiteList: [ https://child.example.com ] });也可以动态管理白名单iframe.addWhiteList(https://trusted.example.com); iframe.removeWhiteList(https://trusted.example.com); console.log(iframe.getWhiteList());开发环境可以使用*但生产环境不建议这样配置iframe.addWhiteList(*);如果允许任意来源通信恶意网站可能向 iframe 发送伪造消息因此不要在通配符环境下传递敏感 Token 或隐私数据。核心特性目前iframe-js主要支持跨域 iframe 双向通信Promise ACK 确认机制离线消息队列握手协议RPC 远程函数调用全局状态同步自动高度适应多 iframe 实例隔离Origin 白名单校验Vue、React 以及原生 JavaScriptTypeScript 类型声明零运行时依赖iframe-js的整体架构和通信链路在线体验可以直接打开 Demo 查看通信过程基础通信与 ACKiFrame.js - Parent Page Demo自动高度适应iFrame.js - Parent Page Demo状态同步与 RPCiFrame.js - Parent Page Demo建议打开浏览器控制台可以看到握手、消息队列和 ACK 回执等通信日志。总结如果项目中只是偶尔发送一条消息原生postMessage已经够用。但当你需要处理以下场景时消息可靠送达iframe 加载时序跨域 RPC状态共享自动高度多实例隔离安全白名单可以尝试使用iframe-js减少重复编写通信基础设施代码。这是我第一次在 CSDN 分享iframe-js。项目还有很多可以改进的地方也可能存在没有覆盖到的业务场景。如果你正在使用欢迎提 Issue 或留下建议如果这个项目对你有帮助也欢迎点一个 Star。GitHub - 1503963513/iFramejs: 解决 IFrame 通信优雅使用 PostMessage · GitHub