iframe通信、跨标签通信的常见方案

前言

项目内嵌 iframe 也是一些大项目,或者久远项目会涉及到的问题,微前端可能遇到的稍微会多一些

此外,这个由于开发过程中并不是那么常用,很多人甚至过一段时间会忘记其通信方案,这里也记录一下与大家共享

同源情况通信

无论是iframe、跨标签页通信,只要是同源,那 iframe通信 基本都可以使用跨标签页通信,iframe 的一些方案却不能用在跨标签页

跨标签页通信(iframe通信亦可)

当然现在单页面SPA应用比较多,跨标签页很多场景都很少用了,了解一下,iframe用的相对就比较多了,并且跨标签页只能同源,否则无法正常手段通信(使用上websocket之类的当我没说😂)

使用 localStorage 通信

localStorage 是 HTML5 提供的一种在浏览器端存储数据的方式,数据存储在用户的浏览器中,同源情况下不同标签页、iframe可以访问相同的 localStorage 对象

浏览器提供了监听 localStorage 的变化事件,以实现标签页之间的通信。

在一个标签页中设置 localStorage 的值:

javascript 复制代码
localStorage.setItem('message', 'Hello from tab 1');

在另一个标签页中监听 localStorage 的变化事件:

javascript 复制代码
window.addEventListener('storage', function(event) {
  if (event.key === 'message') {
    console.log('Received message:', event.newValue);
  }
});

使用 Broadcast Channel API 通信

Broadcast Channel API 允许同源的不同浏览器上下文(如不同标签页、iframe 等)之间进行通信。

在一个标签页中创建 Broadcast Channel 对象并发送消息:

ini 复制代码
const bc = new BroadcastChannel('my_channel');
bc.postMessage('Hello from tab 1');

在另一个标签页中创建相同名称的 Broadcast Channel 对象并监听消息:

javascript 复制代码
const bc = new BroadcastChannel('my_channel');

bc.addEventListener('message', function(event) {
  console.log('Received message:', event.data);
});

可以创建一个 Broadcast Channel 对象,并通过它发送和接收消息。

使用 SharedWorker 通信

SharedWorker 是一种在浏览器后台运行的 Web Worker,可以被多个标签页共享。

标签页可以通过与 SharedWorker 进行通信来实现跨标签页的消息传递。

创建一个 SharedWorker 文件(例如 shared-worker.js):

ini 复制代码
const port = self.addEventListener('connect', function(event) {

  const workerPort = event.ports[0];
  workerPort.addEventListener('message', function(event) {
    // 处理接收到的消息
    workerPort.postMessage('Response from SharedWorker');
  });
  workerPort.start();
});

在标签页中连接 SharedWorker 并发送消息:

ini 复制代码
const sharedWorker = new SharedWorker('shared-worker.js');
const port = sharedWorker.port;
port.postMessage('Hello from tab 1');

port.addEventListener('message', function(event) {
  console.log('Received message:', event.data);
});

同源iframe通信

若 iframe 与父页面同源,可直接通过各自的window 对象相互访问(他们确实不是共用一个window哈,毕竟两个源,共用就很容易出乱子哈)

父页面通过 iframe 元素的 contentWindow 属性获取 iframe 的 window 对象,进而调用其方法或访问变量,这也是比较常见的 iframe 同源通信方案

js 复制代码
// 父页面代码 
const iframeWin = document.getElementById('myIframe').contentWindow; 

iframeWin.iframeMethod('消息内容'); // 调用 iframe 中的函数 

window.parentMethod = () => {
    我是父页面声明的函数
}

iframe 中通过 parenttop 属性获取父页面的 window 对象(top 指向最顶层页面,parent 指向直接父页面)

js 复制代码
// iframe
parent.parentMethod('消息内容'); // 调用父页面中的函数 

window.iframeMethod = () => {
    //我是iframe 中声明的函数
}

同源 iframe 与 iframe 之间通信

上面只讲了 父页面 与 iframe 之间的通信,没有讲 iframe 与 iframe 之间的通信呀

可以思考一下,实际上已经讲了,只不过,只差了一步思路而已

iframe 与 iframe 之间的通信 将父页面当做中间代理类进行交互即可,父类做一下中转即可

做一个例子,iframe1发起支付,由iframe2执行支付,并将支付结果传递给iframe1

js 复制代码
// 父页面代码 
const iframeWin1 = document.getElementById('myIframe').contentWindow; 
const iframeWin2 = document.getElementById('myIframe').contentWindow; 

iframeWin.iframeMethod('消息内容'); // 调用 iframe 中的函数 

window.sendPay = (message) => {
    //iframe2要调用的iframe1的声明的方法
    iframeWin1 && iframeWin1.iframe1Method && iframeWin1.iframe1Method(message)
}
window.payResult = (message) => {
     //iframe1要与iframe2通信,因此要调用iframe2的声明的方法
    iframeWin2 && iframeWin2.iframe1Method && iframeWin2.iframe1Method(message)
}

iframe1代码

js 复制代码
//iframe1
//收到iframe2传递过来的支付结果,也就是iframe2通过父页面通信调用iframe1的payResult方法
window.payResult = () => {
    //我是iframe1中声明的函数
}

parent.sendPay('消息内容'); // 通过父页面通知iframe2发起支付

iframe2代码

js 复制代码
iframe2
//发起支付,由外部也就是iframe1调用,实际是通过iframe1与父页面通信调用iframe2的sendPay方法
window.sendPay = (message) => {

}

parent.payResult(message) //将支付结果通过父页面通知到iframe1

是否看起来很麻烦,实际上不然,我们实际开发中两个模块都是独立的,如果直接使用手段,直接强行操控另一个iframe内容,那么结果将会很难维护(例如:直接获取父页面window方法,直接在当前frame随意获取到其他任何iframe的window、document,直接操控其他iframe,那么后续真的很难维护哈,不要开坏头😂)

实际这种子iframe之间互相交互的,可以另起一个类,比如叫:iframe1WithIframe2(实际可根据业务等起名都没问题,模块之间的交互功能放到一块,也方便维护)

同源其他诡异的通信方式(不推荐)

同源的时候,由于storage、indexDB 等缓存都可以直接访问到,因此有些人开始使用诡异手段了:

等待的一端直接轮训 storage、indexDB等,另外一段通信时更新 storage、indexDB等,更新后,会被轮训端查询到,因此实现的通信

虽然可以实现,但是不推荐哈,这种奇淫技巧属于没有什么其他手段应急的,正常有手段的时候不太推荐哈,其不算是一个正常的通信手段😂

iframe 跨域通信

若 iframe 与父页面不同源(跨域),需使用 postMessage API 进行安全通信

postMessage语法:

  • 发送方调用 window.postMessage(message, targetOrigin),接收方通过监听 message 事件获取消息。
  • message:需发送的数据(字符串、对象等,会被序列化)。
  • targetOrigin:指定接收方的域名(* 表示允许所有域名,但不安全,建议明确指定)。

父页面向 iframe 发送消息

js 复制代码
// 父页面代码 
const iframe = document.getElementById('myIframe'); 

iframe.contentWindow.postMessage('来自父页面的消息', 'https://iframe-domain.com'); // 明确目标域名

iframe 向父页面发送消息

js 复制代码
// iframe 内部代码 
window.parent.postMessage('来自 iframe 的消息', 'https://parent-domain.com'); // 明确父页面域名

ps:有人会觉得使用上面同源window交互方式不行么,可以自行尝试一下哈,非同源出于安全策略,浏览器不会让你拿到 iframe 的 window 信息的😂

最后

就介绍到这里吧,实际上这些也是常见方案,也就是比较简单易懂的通信方案,也是浏览器直接提供的

当有些通信这里都没办法做到的时候,可以尝试使用网络通信了,比如有些人非要跨越两个浏览器,那没辙,可上websocket、http了😂

相关推荐
Entropy-Lee19 分钟前
JavaScript 语句和函数
开发语言·前端·javascript
Wcowin1 小时前
MkDocs文档日期插件【推荐】
前端·mkdocs
xw52 小时前
免费的个人网站托管-Cloudflare
服务器·前端
网安Ruler2 小时前
Web开发-PHP应用&Cookie脆弱&Session固定&Token唯一&身份验证&数据库通讯
前端·数据库·网络安全·php·渗透·红队
!win !2 小时前
免费的个人网站托管-Cloudflare
服务器·前端·开发工具
饺子不放糖2 小时前
基于BroadcastChannel的前端多标签页同步方案:让用户体验更一致
前端
饺子不放糖2 小时前
前端性能优化实战:从页面加载到交互响应的全链路优化
前端
Jackson__2 小时前
使用 ICE PKG 开发并发布支持多场景引用的 NPM 包
前端
饺子不放糖2 小时前
前端错误监控与异常处理:构建健壮的Web应用
前端
cos2 小时前
FE Bits 前端周周谈 Vol.1|Hello World、TanStack DB 首个 Beta 版发布
前端·javascript·css