刚来公司时做得第一个项目是跨部门协作,运用了MessengerJS来做通讯,异常简朴,MessengerJS代码不长,这里剖析一下iframe间通讯的完成体式格局
源码
/**
* __ ___
* / |/ /___ _____ _____ ___ ____ ____ _ ___ _____
* / /|_/ // _ \ / ___// ___// _ \ / __ \ / __ `// _ \ / ___/
* / / / // __/(__ )(__ )/ __// / / // /_/ // __// /
* /_/ /_/ \___//____//____/ \___//_/ /_/ \__, / \___//_/
* /____/
*
* @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,它能够避开同源战略的限制,完成平安的跨域通讯
向外界窗口发送音讯
otherWindow.postMessage(message, targetOrigin);
otherWindow: 指目的窗口,也就是给哪一个window发音讯,是 window.frames 属性的成员或许由 window.open 要领建立的窗口
message: 是要发送的音讯,范例为 String、Object (IE8、9 不支撑),平常运用json数据
targetOrigin: 是限制音讯吸收局限,协定+主机+端口号[+URL],URL会被疏忽,所以能够不写,不限制请运用 ‘*’
接收信息的message事宜
var onmessage = function (event) {
var data = event.data;
var origin = event.origin;
//do someing
};
if (typeof window.addEventListener != 'undefined') {
window.addEventListener('message', onmessage, false);
} else if (typeof window.attachEvent != 'undefined') {
//for ie
window.attachEvent('onmessage', onmessage);
}
注重:ie6/7不支撑postMessage,因此在ie6/7中跨域通讯一般运用window.name
window.name的美好的地方:name 值在差别的页面(以至差别域名)加载后照旧存在,而且能够支撑异常长的 name 值(2MB)
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){
// 发送音讯
};
Messenger类
信使类,建立多个音讯对象,注册多个监听事宜,每个音讯对象的播送音讯会被这个信使类下面的一切监听事宜吸收到
function Messenger(messengerName, projectName){
this.targets = {};
this.name = messengerName;
this.listenFunc = [];
this.initListen();
}
// 增加一个音讯对象
Messenger.prototype.addTarget = function(target, name){};
// 初始化音讯监听
Messenger.prototype.initListen = function(){};
// 监听音讯
Messenger.prototype.listen = function(callback){};
// 注销监听
Messenger.prototype.clear = function(){};
// 播送音讯
Messenger.prototype.send = function(msg){};
完成逻辑是:
initListen要领初始化,将generalCallback回调要领注册到message监听中
addTarget将音讯对象增加到targets对象中
listen要领将监听要领增加到listenFunc数组中
send要领实行每个target对象的send要领
target对象的send要领实行,触发了message监听,触发了generalCallback的实行,从而实行了listenFunc数组中的要领
在postMessage的注册回调要领里加了一个回调要领组listenFunc
在postMessage的监听触发要领外加了一层团体触发对象targets
从而达到了播送的结果
postMessage自身能够完成播送的结果,然则MessengerJS为了兼容,限制了postMessage的才能,自行完成了播送
运用场景
MessengerJS来做iframe通讯处理的最常见的问题是,在主页面为iframe留足高度
parent页面
var messenger = new Messenger('parent');
var iframe = document.getElementById('iframepage');
messenger.addTarget(iframe.contentWindow, 'iframe');
messenger.listen(function (msg) {
var result = parseInt(msg, 10) + 20;
if (result < mainWindowHeight) {
result = mainWindowHeight;
}
$('#iframepage').height(result);
});
iframe页面
// iframe跨域传数据
var messenger = new Messenger('iframe');
messenger.addTarget(window.parent, 'parent');
// 跨域传main 高度
var height = $('.main').height();
messenger.targets['parent'].send(height);
messenger.listen(function (msg) {
});
总结
postMessage是一个用于平安的运用跨源通讯的要领,协助web开辟回归正轨
MessengerJS完成结果很好,即使做频仍的交互,也不会有显著的卡顿,不过时期在提高,今后可能会很少用到如许的兼容了
官方博文看这里:MessengerJS
本文转载自笔者个人博客:Gaoxuefeng’s Blog