一年前,我发过一篇关于跨文档通信方案的文章《iframe跨域通信的通用解决方案》,提供了一种基于创建iframe与轮询window.name的方案。
一年后,很高兴地带来彻底改造的新版本。实际上新方案已经用了很久了,一直没有时间抽象出来,最近终于挤时间分享出来了!~
回望过去
第一版的方案还是有不少问题,这里统一回复与总结一下。第一次使用MessengerJS的同学,可以直接跳到下面的“新版使用”小节。
无法使用的反馈
第一版方案,在一定程度上可以解决iframe通信的问题,但从大家的反馈上看,还是存在一些不足。这里列举一下评论里反馈的问题:
- HTTP与HTTPS,无法通信
- IE6在某些设置下,无法通信
- js设置document.domain后,无法通信
性能有损
第一版方案,需要在内层的iframe中创建两个iframe,并且需要跑定时器轮询window.name,其性能必然有所损耗,更不要说在IE6/7下执行这样的操作。如果父窗口要与两个iframe通信,那么性能的问题也会成倍增长。
API不一致
第一版方案,为父窗口和iframe提供了不同的API。这样的设计并不友好,使用者应该把每个窗口对象统一对待。
多iframe通信?
多个iframe无法直接通信,需要父窗口中转才行。
问题新版都解决了?
那是必须的,上述问题全部得以解决,更重要的是,代码量还减少了50%+!
新方案原理概述
概念上,方案的理念还是使用“信使”概念,即 Messenger。
对于现代浏览器,postMessage API还是无可撼动的。IE6/7下,使用的是一个被认为是bug或安全漏洞的特性,即navigator对象在父窗口和iframe之间是共享的。 基于这一点,我们可以在父窗口中,在navigator对象上注册一个消息回调函数;在iframe中,调用navigator上的这个函数并传入参数。 此时可看作,iframe往父窗口的一个函数传递了一个参数,并在父窗口的上下文中执行了,那么就相当于iframe向父窗口发送了一条消息。反之亦然。
原理就是这么简单(这次我连图都不用画了),好处也是很明显的:
- 该方案不依赖浏览器的各项设计,不受设置影响,同时完美支持HTTPS
- 不用创建多余iframe,基于接口调用,不需要轮询,性能大幅提升
- 良好的接口封装,所有窗口对象统一对待
- 多iframe也不怕,navigator对象的共享,让iframe之间直接通信成为可能
关于安全性
有些同学认为上述方案存在安全风险,也有在wuyun反馈这类问题,但微软并没有去修改。
其实并不用担心,这里做个简单说明:
我们只将消息回调函数注册在navigator对象上,虽然任何引入的脚本或页面,都可以向navigator上发消息,但这其实和 postMessage不限域的情况并无差异。这里对开发者的建议是,传递消息使用JSON String的形式,使用一个字段做消息有效性的验证。如果怕一个固定值(如项目名)不安全,可以使用一个简单的加密算法,并对业务脚本进行压缩混淆,此 时的安全风险可以降到最低。
好处说完了,怎么用?
- 在需要通信的文档中(父窗口和iframe们), 都确保引入MessengerJS
- 每一个文档(
document
), 都需要自己的Messenger
与其他文档通信. 即每一个window
对象都对应着一个, 且仅有一个Messenger
对象, 该Messenger
对象会负责当前window
的所有通信任务. 每个Messenger
对象都需要唯一的名字, 这样它们才可以知道跟谁通信.// 父窗口中 - 初始化Messenger对象 var messenger = new Messenger('Parent'); // iframe中 - 初始化Messenger对象 var messenger = new Messenger('iframe1'); // 多个iframe, 使用不同的名字 var messenger = new Messenger('iframe2');
- 在发送消息前, 确保目标文档已经监听了消息事件.
// iframe中 - 监听消息 // 回调函数按照监听的顺序执行 messenger.listen(function(msg){ alert("收到消息: " + msg); });
- 父窗口想给iframe发消息, 它怎么知道iframe的存在呢? 添加一个消息对象吧.
// 父窗口中 - 添加消息对象, 明确告诉父窗口iframe的window引用与名字 messenger.addTarget(iframe1.contentWindow, 'iframe1'); // 父窗口中 - 可以添加多个消息对象 messenger.addTarget(iframe2.contentWindow, 'iframe2');
- 一切ready, 发消息吧~ 发送消息有两种方式. (以父窗口向iframe发消息为例)
// 父窗口中 - 向单个iframe发消息 messenger.targets['iframe1'].send(msg1); messenger.targets['iframe2'].send(msg2); // 父窗口中 - 向所有目标iframe广播消息 messenger.send(msg);
- 现在看到iframe收到消息的alert提示了吗?
更多
Demo: http://biqing.github.io/labs/messenger/parent.html
项目主页:http://biqing.github.io/MessengerJS/
欢迎反馈,使用中遇到问题一定要告诉我哟!
相关推荐
该文档介绍了vue和普通web页面中iframe实现跨域的解决方案,解决了主页面中无法调用iframe方法的问题
在web开发中,跨域问题是经常遇到的,但是由于浏览器同源策略的限制,不同域之间属性和操作是无法直接交互的。本次讨论iframe和父页面的消息通信。
MessengerJS-master(iframe跨域通信) MessengerJS-master(iframe跨域通信)
通过HTTPClient界面在JSP中嵌入iframe子界面跨域时,无法获取跨域界面的属性值的问题
谷歌跨域插件Access-Control-Allow-Origin
iframe 跨域访问session问题解决方法
这是关于iframe使用过程中出现的问题整理的解决方法,关于使用iframe不用单独写接口打通数据,直接把数据通过ifarme嵌套方法传递过去,使用简单方便。
框架完美解决了iframe之间的跨域通讯。底层技术采用window.name转换代理实现
跨域的解决方案有多重JSONP、Flash、Iframe等,当然还有CORS(跨域资源共享,Cross-Origin Resource Sharing)
js跨域解决方案
单点登录跨域iframe互相通信方案 ,内含详细步骤,可以实现不同源的域名互相通信。亲测,可用哦。也可以到我的博客查看具体操作步骤。
iframe跨域问题:Uncaught DOMException Blocked a frame with origin解决方法
signalR跨域及解决方案 Access-Control-Allow-Origin' header is present之 为什么会跨域及解决方案
使用大家说的以下配置,验证无效,跨域问题仍然存在 add_header 'Access-Control-Allow-Origin' '*'; add_header 'Access-Control-Allow-Credentials' 'true'; add_header 'Access-Control-Allow-Methods' 'GET,...
NULL 博文链接:https://hqlly.iteye.com/blog/1662337
以前在面试的时候经常遇到问关于跨域的事儿,所以自己对跨域有一定的概念性了解,知道什么是跨域以及解决跨域的方法,但是具体实际从来没有操作过,直到最近在公司项目中,遇到了一个需要使iframe跨域进行POST提交的...
现在的web工程越来也大,传统的开发模式已经显得捉襟见肘了,不仅开发人员在开发过程中很痛苦,后期维护的人员也更痛苦。...下面我们介绍一种测地解决前端跨域访问的方式,本地服务器请求转发的方式。
该资源为ASP.NET网站开发技术中使用Ajax进行跨域请求时利用JSONP方案解决浏览器同源策略限制的源码,比较简单,仅供参考
NULL 博文链接:https://lililucky1211.iteye.com/blog/1853504