做两个不同的项目,iframe嵌入并跨域传输数据;之前写过一篇文章,如何简单的处理跨域的问题,及一种比较笨拙的解决方案;以中间页面(html),做数据传输的中转站;
这次发现了一个插件,MessengerJS 来做数据通信,才发现这个封装好的插件做这种跨域的方式其实很简单;这里就简单的分析 一下这MessengerJS,代码比较少,没那么多140行加注释;
主要是用到H5的通信协议postMessage()方法做数据传输;postMessage我会写一篇日志了解一下,现在先解析一下MessengerJS;
MessengerJS源码:
/**
* __ ___
* / |/ /___ _____ _____ ___ ____ ____ _ ___ _____
* / /|_/ // _ \ / ___// ___// _ \ / __ \ / __ `// _ \ / ___/
* / / / // __/(__ )(__ )/ __// / / // /_/ // __// /
* /_/ /_/ \___//____//____/ \___//_/ /_/ \__, / \___//_/
* /____/
*
* @description MessengerJS, a common cross-document communicate solution.
* @author biqing kwok
* @version 2.0
* @license release under MIT license
*/
window.Messenger = (function(){
// 消息前缀, 建议使用自己的项目名, 避免多项目之间的冲突
// !注意 消息前缀应使用字符串类型
var prefix = "[PROJECT_NAME]",
supportPostMessage = 'postMessage' in window;
// Target 类, 消息对象
function Target(target, name, prefix){
var errMsg = '';
if(arguments.length < 2){
errMsg = 'target error - target and name are both required';
} else if (typeof target != 'object'){
errMsg = 'target error - target itself must be window object';
} else if (typeof name != 'string'){
errMsg = 'target error - target name must be string type';
}
if(errMsg){
throw new Error(errMsg);
}
this.target = target;
this.name = name;
this.prefix = prefix;
}
// 往 target 发送消息, 出于安全考虑, 发送消息会带上前缀
if ( supportPostMessage ){
// IE8+ 以及现代浏览器支持
Target.prototype.send = function(msg){
this.target.postMessage(this.prefix + '|' + this.name + '__Messenger__' + msg, '*');
};
} else {
// 兼容IE 6/7
Target.prototype.send = function(msg){
var targetFunc = window.navigator[this.prefix + this.name];
if ( typeof targetFunc == 'function' ) {
targetFunc(this.prefix + msg, window);
} else {
throw new Error("target callback function is not defined");
}
};
}
// 信使类
// 创建Messenger实例时指定, 必须指定Messenger的名字, (可选)指定项目名, 以避免Mashup类应用中的冲突
// !注意: 父子页面中projectName必须保持一致, 否则无法匹配
function Messenger(messengerName, projectName){
this.targets = {};
this.name = messengerName;
this.listenFunc = [];
this.prefix = projectName || prefix;
this.initListen();
}
// 添加一个消息对象
Messenger.prototype.addTarget = function(target, name){
var targetObj = new Target(target, name, this.prefix);
this.targets[name] = targetObj;
};
// 初始化消息监听
Messenger.prototype.initListen = function(){
var self = this;
var generalCallback = function(msg){
if(typeof msg == 'object' && msg.data){
msg = msg.data;
}
var msgPairs = msg.split('__Messenger__');
var msg = msgPairs[1];
var pairs = msgPairs[0].split('|');
var prefix = pairs[0];
var name = pairs[1];
for(var i = 0; i < self.listenFunc.length; i++){
if (prefix + name === self.prefix + self.name) {
self.listenFunc[i](msg);
}
}
};
if ( supportPostMessage ){
if ( 'addEventListener' in document ) {
window.addEventListener('message', generalCallback, false);
} else if ( 'attachEvent' in document ) {
window.attachEvent('onmessage', generalCallback);
}
} else {
// 兼容IE 6/7
window.navigator[this.prefix + this.name] = generalCallback;
}
};
// 监听消息
Messenger.prototype.listen = function(callback){
var i = 0;
var len = this.listenFunc.length;
var cbIsExist = false;
for (; i < len; i++) {
if (this.listenFunc[i] == callback) {
cbIsExist = true;
break;
}
}
if (!cbIsExist) {
this.listenFunc.push(callback);
}
};
// 注销监听
Messenger.prototype.clear = function(){
this.listenFunc = [];
};
// 广播消息
Messenger.prototype.send = function(msg){
var targets = this.targets,
target;
for(target in targets){
if(targets.hasOwnProperty(target)){
targets[target].send(msg);
}
}
};
return Messenger;
})();
分析:
SUPPORTPOSTMESSAGE变量:
用来检测当前浏览器是否支持postMessage;postMessage是HTML5引入的通信API,可以实现安全的跨域通信像外界窗口发送消息;
注意:ie6/7不支持postMessage,因此在ie6/7中跨域通信通常使用window.name
window.navigator有与window.name类似的特性,而且可以保存回调方法
MessengerJS的实现思路是高级浏览器使用postMessage,不支持postMessage的使用window.navigator来保存回调方法
Target类:
消息类,发送执行者;
function Target(target, name){
this.target = target;
this.name = name;
}
/**
* 消息发送方法
*/
Target.prototype.send = function(msg){
// 发送消息
this.target.postMessage(this.name);
};
Messenger类:
信使类,创建多个消息对象,注册多个监听事件,每一个消息对象的广播消息会被这个信使类下面的所有监听事件接收到;这个类主要的作用就是用来做监听跨域时传过来的信息;
// 创建消息类
Messenger.prototype.addTarget(target, name);//通过target消息类,创建一个消息类对象;
// 初始化消息监听
Messenger.prototype.initListen = function(){};
// 监听消息
Messenger.prototype.listen = function(callback){};
// 注销监听
Messenger.prototype.clear = function(){};
// 广播消息
// 传输的数据msg,格式可以自定义,需要父页面子页面规定好结构,也可以添加些校验
Messenger.prototype.send = function(msg){};
例子:
当父页面禁用iframe滚动条时,通过子页面传过来的高度,实现父页面动态滚动效果;
iframe父页面:
// 创建messenger对象的参数,iframe_parent,在子页面中创建的addTarget(target,name)对象方法中的
// name参数值需要保持一致
var messenger = new Messenger('iframe_parent');
var iframe = document.getElementById('iframe1');
messenger.addTarget(iframe.contentWindow, 'iframe');
messenger.listen(function (msg) {
// 这里可以对传过来的参数做校验;
var result = Number(msg);
$('body,html').animate({scrollTop: msg}, 500);
});
iframe嵌入的子页面:
// iframe跨域传数据
var messenger = new Messenger('iframe');
// 参数iframe_parent要与父页面创建的messenger对象参数名保持一致
messenger.addTarget(window.parent, 'iframe_parent');
// 跨域传main 高度
var height = $('.main').height();
messenger.targets['parent'].send(height);
// 子页面也可以做监听
messenger.listen(function (msg) {
//...
});
总结:
跨域的方法有很多,找到自己合适的才是最好的;