前言
项目内嵌 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 中通过 parent
或 top
属性获取父页面的 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了😂