在前端开发项目中,不可避免的总会和 iframe 进行打交道,我们通常会使用 postMessage 实现消息通讯。
如果存在下面情况:
iframe 父子通讯iframe 同层级通讯iframe 嵌套层级通讯当面对这种复杂的情况的时候,通讯不可避免成为复杂问题。
为了解决这复杂的问题,我开发了 iframe-bridge 来帮助大家优雅的解决这类问题。
npm install bridge-iframe
# pnpm
pnpm install bridge-iframe
# yarn
yarn add bridge-iframe
Main
Main/Node1主页面(Main)
<h1>Mainh1>
<iframe src="Node1.html" id="Node1">iframe>
import { IFrameBridge, IFrameMessage } from 'bridge-iframe';
// 创建桥接对象
const bridge = new IFrameBridge;
// 连接直接下属节点 Node1 关联 iframe 窗口
birdge.ifrme('Node1', document.getElementById('Node1'));
// 提供给其他 iframe 节点调用的方法(可以定义无数个)
birdge.on('say', async (vo: IFrameMessage) => {
vo.getData(); // 获取请求数据
vo.getResult(); // 获取响应数据
return '来自于 Main';
});
// 等待桥接初始化完成
birdge.ready(async () => {
console.log('Main 初始化完成!!!');
});
// 等待 Node1 节点桥接完成
birdge.ready('Node1', async () => {
console.log('Watch Node1 初始化完成!!!');
// 请求 Node1 的 say 方法
birdge.request({
name: 'Node1',
method: 'say',
}).then((vo: any) => {
console.log('在 Main 中请求 Node1.say 方法', vo);
}).catch((err: any) => {
console.log('出现错误', err);
});
});
// 窗口销毁时
bridge.destroy();
子页面(Node1)
<h1>Node1h1>
import { IFrameBridge } from 'bridge-iframe';
// 创建桥接对象
const bridge = new IFrameBridge({ name: 'Node1' });
// 提供给其他 iframe 节点调用的方法(可以定义无数个)
birdge.on('say', async (vo: IFrameMessage) => {
return '来自于 Nodeq';
});
// 等待桥接初始化完成
birdge.ready(async () => {
console.log('Node1 初始化完成!!!');
});
// 等待 Node1 节点桥接完成
birdge.ready('Main', async () => {
console.log('Watch Main 初始化完成!!!');
// 请求 Main 的 say 方法
birdge.request({
name: 'Main',
method: 'say',
}).then((vo: any) => {
console.log('在 Node1 中请求 Main.say 方法', vo);
}).catch((err: any) => {
console.log('出现错误', err);
});
});
// 窗口销毁时
bridge.destroy();
其中关于请求 name 在这里称呼为 iframe node 的 域名 作为通讯标识。
关于子节点的名称可以为任意名称,但有两类名称是内置的代表特殊作用不能被使用。
Main 作为 主节点/主窗口 的名称地址Parent 作为只请求上一级节点的名称标识,不管上层节点名字是什么Main
Main/Node1
Main/Node1/Node1-1Main/Node1/Node1-2Main/Node2
Main/Node2/Node2-1Main/Node2/Node2-2在这里还是一样的,创建主页面桥接对象,并关联子页面 iframe 相对的子页面也创建有名称的桥接对象。
还是通过注册一些可以被其他节点调用的方法来实现双通讯的。
这里参考了计算机网络的 交换机 的模式来实现跨层级转发。
/——> (子节点1)
(父节点) <———> (节点) <——————> (子节点2)
\——> (子节点n) ...
节点 都有 上级节点x1 和 下级节点xN 的结构。postMessage 来实现。节点 的时候,通过 message.path 判断 message 是向上 window.parent.postMessage() 传递还是向下 iframe.contentWindow.postMessage() 传递。节点 的时候,会记录经过的路径为 tracks{ 节点名称, 转发方向 }[] 以此来实现初始地址分配,以及消息返回路径确认。为了实现跨层级通讯,动态为 节点 分配地址,得实现 节点名称映射地址库 来实现。
@bridge/online 通知主窗口注册地址@bridge/domain 获取名称映射地址@bridge/mapping 获取所有映射地址@bridge/ready 节点准备好了吗?为了方便调用,定义了如下内置地址:
Main 请求主窗口地址Parent 向上级请求窗口(无论层级高低都向上级请求)页面层级
Main
Main/Node1
Main/Node1/Node1-1Main/Node1/Node1-2Main/Node2
Main/Node2/Node2-1Main/Node2/Node2-2向上请求 Main/Node1/Node1-1 到 Main
<内置协议获取地址>Main/Node1/Node1-1 请求 ↑↑↑ 到 Main/Node1
{Node1-1:U}]Main/Node1 转发 ↑↑↑ 到 Main
{Node1-1:U}, {Node1:U}]Main 处理逻辑Main 响应 ↓↓↓ 到 Main/Node1
{Node1-1:U}]Main/Node1 转发 ↓↓↓ 到 Main/Node1/Node1-1
Main/Node1/Node1-1 收到响应向下请求 Main 到 Main/Node1/Node1-1
<内置协议获取地址>Main 请求 ↓↓↓ 到 Main/Node1
{Main:D}]Main/Node1 转发 ↓↓↓ 到 Main/Node1/Node1-1
{Main:D}, {Node1:D}]Main/Node1/Node1-1 处理逻辑Main/Node1/Node1-1 响应 ↑↑↑ 到 Main/Node1
{Main:D}]Main/Node1 转发 ↑↑↑ 到 Main
Main 收到响应同级请求 Main/Node1/Node1-1 到 Main/Node1/Node1-2
<内置协议获取地址>Main/Node1/Node1-1 请求 ↑↑↑ 到 Main/Node1
{Node1-1:U}]Main/Node1 转发 ↓↓↓ 到 Main/Node1/Node1-2
{Node1-1:U}, {Node1:D}]]Main/Node1/Node1-2 处理逻辑Main/Node1/Node1-2 响应 ↑↑↑ 到 Main/Node1
{Node1-1:U}]Main/Node1 转发 ↓↓↓ 到 Main/Node1/Node1-1
Main/Node1/Node1-1 收到响应跨级请求 Main/Node1/Node1-1 到 Main/Node2/Node2-1
<内置协议获取地址>Main/Node1/Node1-1 请求 ↑↑↑ 到 Main/Node1
{Node1-1:U}]Main/Node1 转发 ↑↑↑ 到 Main
{Node1-1:U}, {Node1:U}]Main 转发 ↓↓↓ 到 Main/Node2
{Node1-1:U}, {Node1:U}, {Main:D}]Main/Node2 转发 ↓↓↓ 到 Main/Node2/Node2-1
{Node1-1:U}, {Node1:U}, {Main:D}, {Node2:D}]Main/Node2/Node2-1 处理逻辑Main/Node2/Node2-1 响应 ↑↑↑ 到 Main/Node2
{Node1-1:U}, {Node1:U}, {Main:D}]Main/Node2 转发 ↑↑↑ 到 Main
{Node1-1:U}, {Node1:U}]Main 转发 ↓↓↓ 到 Main/Node1
{Node1-1:U}]Main/Node1 转发 ↓↓↓ 到 Main/Node1/Node1-1
Main/Node1/Node1-1 收到响应