如何优雅的实现 iframe 多层级嵌套通讯

devtools/2024/10/21 4:31:12/

前言

在前端开发项目中,不可避免的总会和 iframe 进行打交道,我们通常会使用 postMessage 实现消息通讯

如果存在下面情况:

当面对这种复杂的情况的时候,通讯不可避免成为复杂问题。

图片

快速开始

为了解决这复杂的问题,我开发了 iframe-bridge 来帮助大家优雅的解决这类问题。

npm install bridge-iframe
# pnpm
pnpm install bridge-iframe
# yarn
yarn add bridge-iframe

假设页面层级如下:

  • Main
    • Main/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 作为只请求上一级节点的名称标识,不管上层节点名字是什么

假设页面层级如下(更复杂):

  • Main

    • Main/Node1
      • Main/Node1/Node1-1
      • Main/Node1/Node1-2
    • Main/Node2
      • Main/Node2/Node2-1
      • Main/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-1
      • Main/Node1/Node1-2
    • Main/Node2
      • Main/Node2/Node2-1
      • Main/Node2/Node2-2

向上请求 Main/Node1/Node1-1Main

  • <内置协议获取地址>
  • Main/Node1/Node1-1 请求 ↑↑↑Main/Node1
    • tracks[{Node1-1:U}]
  • Main/Node1 转发 ↑↑↑Main
    • tracks[{Node1-1:U}, {Node1:U}]
  • Main 处理逻辑
  • Main 响应 ↓↓↓Main/Node1
    • tracks[{Node1-1:U}]
  • Main/Node1 转发 ↓↓↓Main/Node1/Node1-1
    • tracks[]
  • Main/Node1/Node1-1 收到响应

向下请求 MainMain/Node1/Node1-1

  • <内置协议获取地址>
  • Main 请求 ↓↓↓Main/Node1
    • tracks[{Main:D}]
  • Main/Node1 转发 ↓↓↓Main/Node1/Node1-1
    • tracks[{Main:D}, {Node1:D}]
  • Main/Node1/Node1-1 处理逻辑
  • Main/Node1/Node1-1 响应 ↑↑↑Main/Node1
    • tracks[{Main:D}]
  • Main/Node1 转发 ↑↑↑Main
    • tracks[]
  • Main 收到响应

同级请求 Main/Node1/Node1-1Main/Node1/Node1-2

  • <内置协议获取地址>
  • Main/Node1/Node1-1 请求 ↑↑↑Main/Node1
    • tracks[{Node1-1:U}]
  • Main/Node1 转发 ↓↓↓Main/Node1/Node1-2
    • tracks[{Node1-1:U}, {Node1:D}]]
  • Main/Node1/Node1-2 处理逻辑
  • Main/Node1/Node1-2 响应 ↑↑↑Main/Node1
    • tracks[{Node1-1:U}]
  • Main/Node1 转发 ↓↓↓Main/Node1/Node1-1
    • tracks[]
  • Main/Node1/Node1-1 收到响应

跨级请求 Main/Node1/Node1-1Main/Node2/Node2-1

  • <内置协议获取地址>
  • Main/Node1/Node1-1 请求 ↑↑↑Main/Node1
    • tracks[{Node1-1:U}]
  • Main/Node1 转发 ↑↑↑Main
    • tracks[{Node1-1:U}, {Node1:U}]
  • Main 转发 ↓↓↓Main/Node2
    • tracks[{Node1-1:U}, {Node1:U}, {Main:D}]
  • Main/Node2 转发 ↓↓↓Main/Node2/Node2-1
    • tracks[{Node1-1:U}, {Node1:U}, {Main:D}, {Node2:D}]
  • Main/Node2/Node2-1 处理逻辑
  • Main/Node2/Node2-1 响应 ↑↑↑Main/Node2
    • tracks[{Node1-1:U}, {Node1:U}, {Main:D}]
  • Main/Node2 转发 ↑↑↑Main
    • tracks[{Node1-1:U}, {Node1:U}]
  • Main 转发 ↓↓↓Main/Node1
    • tracks[{Node1-1:U}]
  • Main/Node1 转发 ↓↓↓Main/Node1/Node1-1
    • tracks[]
  • Main/Node1/Node1-1 收到响应

http://www.ppmy.cn/devtools/16734.html

相关文章

超级详细的电脑通过网线连接树莓派操作步骤,以及无法查询到树莓派ip的解决办法(本人亲自踩坑无数总结而来,学不会来揍我系列)

一、正常操作流程 1.首先下载官方烧录系统软件。 Raspberry Pi OS – Raspberry Pi&#xff08;软件下载地址&#xff09; 选择Windows类型&#xff08;这里如果不是Windows用户的友友们可以不用看了&#xff0c;因为后面是基于Windows的远程操控来实现的&#xff0c;苹果的…

【Hadoop】-Apache Hive概述 Hive架构[11]

目录 Apache Hive概述 一、分布式SQL计算-Hive 二、为什么使用Hive Hive架构 一、Hive组件 Apache Hive概述 Apache Hive是一个在Hadoop上构建的数据仓库基础设施&#xff0c;它提供了一个SQL-Like查询语言来分析和查询大规模的数据集。Hive将结构化查询语言&#xff08;…

NVIDIA智算中心“产品”上市,AI工业革命的iPhone时刻

GTC 2024落下帷幕了&#xff0c;但这个大会的信息仍在AI产业和经济中发酵。咨询机构WIKIBON认为&#xff0c;GTC 2024在整个科技史中的意义超过了当年史蒂夫乔布斯的iPod和iPhone发布。在AI将永久改变人类的共识下&#xff0c;GTC 2024在广度、愿景、生态系统等方面都有着深远影…

OpenCV 实现霍夫圆变换

返回:OpenCV系列文章目录&#xff08;持续更新中......&#xff09; 上一篇&#xff1a;OpenCV实现霍夫变换 下一篇:OpenCV 实现重新映射 目标 在本教程中&#xff0c;您将学习如何&#xff1a; 使用 OpenCV 函数 HoughCircles()检测图像中的圆圈。 理论 Hough 圆变换 H…

Java在物联网和嵌入式系统中的应用

Java在物联网和嵌入式系统中的应用 一、引言 随着信息技术的快速发展&#xff0c;物联网和嵌入式系统已经成为当今社会的热点领域。它们为我们的生活带来了前所未有的便利和智能化。而Java作为一种功能强大、跨平台的编程语言&#xff0c;在物联网和嵌入式系统中的应用也日渐…

WPF之Button组件

button是WPF中的按钮控件&#xff0c;通常配合click执行一系列操作。 Button控件的属性 Content: 内容。 Height: 高度。 Width: 宽度。 Margin: 外边距 Padding: 内边距 HorizontalAlignment 和 VerticalAlignment: 按钮内容的水平和对齐方式以及垂直对齐方式。 Background 和…

56、回溯-子集

思路&#xff1a; 子集很好理解&#xff0c;当然也要包括空和本身。其他的就是当前数组的一部分元素了。如何解决呢&#xff1f; 对于当前值来说&#xff0c;我可以不要&#xff0c;也可以要&#xff0c;两种方式&#xff0c;下一个也是&#xff0c;一直到indexlength为止&am…

OpenHarmony语言基础类库【@ohos.util.HashMap (非线性容器HashMap)】

HashMap底层使用数组链表红黑树的方式实现&#xff0c;查询、插入和删除的效率都很高。HashMap存储内容基于key-value的键值对映射&#xff0c;不能有重复的key&#xff0c;且一个key只能对应一个value。 HashMap和[TreeMap]相比&#xff0c;HashMap依据键的hashCode存取数据&…