UNPKG

@ticatec/iframe-message-bridge

Version:

A lightweight TypeScript library for reliable communication between parent window and multiple iframes using postMessage, supporting one-way messages, request-response patterns, broadcast messaging, timeout handling, and automatic resource cleanup.

289 lines (187 loc) 7.44 kB
# iframe-message-bridge [![Version](https://img.shields.io/npm/v/@ticatec/iframe-message-bridge)](https://www.npmjs.com/package/@ticatec/iframe-message-bridge) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) 中文 [English](./README.md) 一个轻量级的 TypeScript 库,基于 `postMessage` 实现父页面与多个 iframe 之间的结构化、可靠通信,支持单向消息、请求-响应模式和广播消息,自动追踪消息并校验来源。 --- ## 📦 特性 - 支持 **父页面与 iframe 之间的单向或双向通信** - 🔁 **请求-响应通信**,使用 `Promise` 封装 - 📡 **广播消息**:父页面可向所有 iframe 同时发送消息 - 🧩 **支持多个 iframe**:父页面可区分每个 iframe 的消息来源 - 🔄 **自动发现 iframe**:无需手动注册,自动扫描页面中的所有 iframe - 🔐 **安全通信**:仅响应受信任的来源(origin) - 🧼 无依赖,轻量高效 --- ## 🚀 安装 ```bash npm install @ticatec/iframe-message-bridge ``` --- ## 🧠 工作原理 * `MessageBridgeManager` 用于 **父页面**,接收 iframe 发来的消息并响应,同时可向所有 iframe 广播消息 * `MessageBridgeClient` 用于 **iframe 中**,向父页面发送消息、等待响应,以及接收广播消息 --- ## 🔧 使用示例 ### 在父页面中 ```ts import { MessageBridgeManager } from 'iframe-message-bridge'; const bridge = new MessageBridgeManager(); // 注册请求-响应事件处理器 bridge.on('getUserInfo', (data, sourceWindow, sourceOrigin) => { console.log('收到来自 iframe 的数据:', data); return { name: 'Alice', role: 'admin' }; }); // 注册广播消息处理器(可选,父页面也能接收广播) bridge.onBroadcast('system-notification', (data) => { console.log('收到系统通知:', data); }); // 向所有 iframe 广播消息 bridge.broadcast('user-login', { userId: 123, userName: 'Alice', timestamp: Date.now() }); // 广播主题变更 bridge.broadcast('theme-change', { theme: 'dark' }); ``` --- ### 在 iframe 中 ```ts import { MessageBridgeClient } from 'iframe-message-bridge'; const bridge = new MessageBridgeClient('https://your-parent-domain.com'); // 发起请求并等待响应 bridge.emit('getUserInfo', { id: 123 }).then(response => { console.log('收到来自父页面的响应:', response); }).catch(error => { console.error('请求失败或超时:', error); }); // 发起请求并设置自定义超时时间(10秒) bridge.emit('slowOperation', { data: 'large' }, 10000).then(response => { console.log('收到响应:', response); }).catch(error => { if (error.message.includes('timeout')) { console.error('请求在10秒后超时'); } }); // 发送单向消息(不需要响应) bridge.send('logEvent', { action: 'opened-page' }); // 监听父页面的广播消息 bridge.onBroadcast('user-login', (data) => { console.log('用户登录广播:', data); updateUserInfo(data); }); bridge.onBroadcast('theme-change', (data) => { console.log('主题变更广播:', data); applyTheme(data.theme); }); // 取消监听特定广播事件 bridge.offBroadcast('theme-change'); // 清空所有广播监听器 bridge.clearBroadcastHandlers(); // 组件卸载时清理资源(内存管理很重要) useEffect(() => { return () => { bridge.destroy(); // 移除所有监听器并清理资源 }; }, []); ``` --- ## 📌 API 参考 ### `MessageBridgeManager`(父页面) #### `new MessageBridgeManager()` 初始化桥接器,注册全局 `message` 事件监听。 #### `.on(eventName: string, handler: (data, sourceWindow, sourceOrigin) => any)` 注册请求-响应事件处理器,当某个 iframe 发送该事件名的消息时被触发,可返回数据作为响应。 #### `.onBroadcast(eventName: string, handler: (data) => void)` 注册广播消息处理器,当接收到广播消息时被触发(可选功能)。 #### `.broadcast(eventName: string, data: any, targetOrigin?: string)` 向页面中所有 iframe 发送广播消息。会自动扫描并获取当前页面的所有 iframe。 - `eventName`: 事件名称 - `data`: 要发送的数据 - `targetOrigin`: 目标域名,默认为 `'*'` #### `.off(eventName: string)` 注销特定的请求-响应事件处理器。 #### `.offBroadcast(eventName: string)` 注销特定的广播消息处理器。 #### `.clearHandlers()` 清空所有请求-响应事件处理器。 #### `.clearBroadcastHandlers()` 清空所有广播消息处理器。 #### `.destroy()` 销毁管理器实例,移除全局消息监听器并清空所有处理器。 --- ### `MessageBridgeClient`(iframe 页面) #### `new MessageBridgeClient(targetOrigin: string)` 创建客户端实例,指定父页面的 origin(例如 `'https://example.com'`) #### `.emit(eventName: string, data?: any, timeout?: number): Promise<any>` 发送一个请求类型的消息,并等待父页面响应。 - `eventName`: 事件名称 - `data`: 要发送的数据(可选) - `timeout`: 超时时间(毫秒),默认为30000ms(30秒) #### `.send(eventName: string, data?: any): void` 发送一个单向消息,不等待响应。 #### `.onBroadcast(eventName: string, handler: (data) => void)` 注册广播消息处理器,监听来自父页面的广播消息。 #### `.offBroadcast(eventName: string)` 取消注册特定的广播消息处理器。 #### `.clearBroadcastHandlers()` 清空所有广播消息处理器。 #### `.clearPendingRequests()` 清空所有待处理的请求并拒绝其 Promise 以防止内存泄漏。 #### `.destroy()` 销毁客户端实例,移除全局消息监听器,清空所有待处理请求和广播处理器。 --- ## 🌟 通信模式 ### 1. 请求-响应模式(iframe → 父页面) ```ts // iframe const result = await bridge.emit('getData', { id: 123 }); // 父页面中 bridge.on('getData', (data) => { return fetchDataById(data.id); }); ``` ### 2. 单向消息(iframe → 父页面) ```ts // iframe bridge.send('analytics', { event: 'page_view' }); // 父页面中 bridge.on('analytics', (data) => { trackEvent(data.event); // 无需返回值 }); ``` ### 3. 广播消息(父页面 → 所有 iframe) ```ts // 父页面中 bridge.broadcast('global-update', { version: '2.0' }); // 所有 iframe bridge.onBroadcast('global-update', (data) => { console.log('收到全局更新:', data.version); }); ``` --- ## 🛡️ 安全建议 * **务必在父页面中校验 `event.origin`**,以防止恶意 iframe 攻击。 * **避免在生产中使用 `"*"` 作为 `targetOrigin`**,请指定明确域名。 * 推荐给 iframe 添加 `sandbox` 属性并限制权限。 * 对于敏感数据的广播,建议在 iframe 中验证消息来源。 --- ## 🔄 自动 iframe 发现 库会自动扫描页面中的所有 `<iframe>` 元素,无需手动注册: ```ts // 父页面中,这些 iframe 都会自动接收广播消息 // <iframe src="module1.html"></iframe> // <iframe src="module2.html"></iframe> // <iframe src="module3.html"></iframe> bridge.broadcast('config-update', newConfig); // 所有 iframe 都会收到 ``` 动态添加的 iframe 也会在下次广播时被自动发现。 --- ## 📜 许可证 [MIT](./LICENSE) --- ## ✨ 作者 Henry Feng 开发 huili.f@gmail.com