前言
在前端开发项目中,不可避免的总会和 iframe 进行打交道,我们通常会使用 postMessage 实现消息通讯。
如果存在下面情况:
iframe父子通讯iframe同层级通讯iframe嵌套层级通讯
当面对这种复杂的情况的时候,通讯不可避免成为复杂问题。
快速开始
为了解决这复杂的问题,我开发了 iframe-bridge 来帮助大家优雅的解决这类问题。
npm install bridge-iframe
# pnpm
pnpm install bridge-iframe
# yarn
yarn add bridge-iframe
假设页面层级如下:
MainMain/Node1
主页面(Main)
<h1>Main</h1>
<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>Node1</h1>
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作为只请求上一级节点的名称标识,不管上层节点名字是什么
假设页面层级如下(更复杂):
-
MainMain/Node1Main/Node1/Node1-1Main/Node1/Node1-2
Main/Node2Main/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向上级请求窗口(无论层级高低都向上级请求)
通讯模拟:
页面层级
MainMain/Node1Main/Node1/Node1-1Main/Node1/Node1-2
Main/Node2Main/Node2/Node2-1Main/Node2/Node2-2
向上请求 Main/Node1/Node1-1 到 Main
<内置协议获取地址>Main/Node1/Node1-1请求 ↑↑↑ 到Main/Node1- tracks[
{Node1-1:U}]
- tracks[
Main/Node1转发 ↑↑↑ 到Main- tracks[
{Node1-1:U}, {Node1:U}]
- tracks[
Main处理逻辑Main响应 ↓↓↓ 到Main/Node1- tracks[
{Node1-1:U}]
- tracks[
Main/Node1转发 ↓↓↓ 到Main/Node1/Node1-1- tracks[]
Main/Node1/Node1-1收到响应
向下请求 Main 到 Main/Node1/Node1-1
<内置协议获取地址>Main请求 ↓↓↓ 到Main/Node1- tracks[
{Main:D}]
- tracks[
Main/Node1转发 ↓↓↓ 到Main/Node1/Node1-1- tracks[
{Main:D}, {Node1:D}]
- tracks[
Main/Node1/Node1-1处理逻辑Main/Node1/Node1-1响应 ↑↑↑ 到Main/Node1- tracks[
{Main:D}]
- tracks[
Main/Node1转发 ↑↑↑ 到Main- tracks[]
Main收到响应
同级请求 Main/Node1/Node1-1 到 Main/Node1/Node1-2
<内置协议获取地址>Main/Node1/Node1-1请求 ↑↑↑ 到Main/Node1- tracks[
{Node1-1:U}]
- tracks[
Main/Node1转发 ↓↓↓ 到Main/Node1/Node1-2- tracks[
{Node1-1:U}, {Node1:D}]]
- tracks[
Main/Node1/Node1-2处理逻辑Main/Node1/Node1-2响应 ↑↑↑ 到Main/Node1- tracks[
{Node1-1:U}]
- tracks[
Main/Node1转发 ↓↓↓ 到Main/Node1/Node1-1- tracks[]
Main/Node1/Node1-1收到响应
跨级请求 Main/Node1/Node1-1 到 Main/Node2/Node2-1
<内置协议获取地址>Main/Node1/Node1-1请求 ↑↑↑ 到Main/Node1- tracks[
{Node1-1:U}]
- tracks[
Main/Node1转发 ↑↑↑ 到Main- tracks[
{Node1-1:U}, {Node1:U}]
- tracks[
Main转发 ↓↓↓ 到Main/Node2- tracks[
{Node1-1:U}, {Node1:U}, {Main:D}]
- tracks[
Main/Node2转发 ↓↓↓ 到Main/Node2/Node2-1- tracks[
{Node1-1:U}, {Node1:U}, {Main:D}, {Node2:D}]
- tracks[
Main/Node2/Node2-1处理逻辑Main/Node2/Node2-1响应 ↑↑↑ 到Main/Node2- tracks[
{Node1-1:U}, {Node1:U}, {Main:D}]
- tracks[
Main/Node2转发 ↑↑↑ 到Main- tracks[
{Node1-1:U}, {Node1:U}]
- tracks[
Main转发 ↓↓↓ 到Main/Node1- tracks[
{Node1-1:U}]
- tracks[
Main/Node1转发 ↓↓↓ 到Main/Node1/Node1-1- tracks[]
Main/Node1/Node1-1收到响应








![Navicat连接SQLSever报错:[08001] MicrosoftTCP Provider 远程主机强迫关闭了一个现有的连接](https://img-blog.csdnimg.cn/direct/3042b73736dc460eb532fe730002166b.png)










