前言
前端在页面嵌入其他站点的页面时,常常会遇到跨域通信的问题,浏览器为了安全问题,限制了不同域名的JS直接调用。
解决方案
浏览器再限制跨域调用的同时,也预留了一个比较安全的通道。那就是message通道。 只要得到一个页面的window对象,都可以调用其postMessage的api,而不会有任何限制。而希望接受到其他页面message的页面只需要侦听这一消息即可。
发送者代码示例
var parentWindow = window.parent; //假设这是一个放在iframe的页面,那么这样可以得到其父页面的window对象
parentWindow.postMessage({
data: 'test'
}, '*')
侦听者代码示例
window.addEventListener('message', function(event) {
}, false);
经过以上几行简单的代码,就完成一个基本的页面通信了。
写一个通用的通信库
接下来对以上的代码进行改良,使其成为一个通用的iframe通信库。
代理端 iframe-proxy.js的内容
var IFRAME_PROXY = {
invoke: function(ns, params, callback, source, domain) {
try {
var arr = ns.split('.');
var target = window;
for (var i = 0; i < arr.length; i++) {
var t = arr[i]
target = target[t];
}
} catch (error) {
console.log(ns + 'not found, check your method.');
return;
}
params.success = function(res) {
source.postMessage({
'type': 'QN_PROXY_CALLBACK',
'params': res,
'callback': callback,
'code': 200
}, domain);
}
params.error = function(res) {
source.postMessage({
'type': 'IFRAME_PROXY_CALLBACK',
'params': res,
'callback': callback,
'code': 999
}, domain);
};
target.invoke(params);
}
}
window.addEventListener('message', function(event) {
//console.log('received response: ', event.data);
var data = event.data;
var domain = event.origin;
var source = event.source;
switch (data.type) {
case 'IFRAME_PROXY':
IFRAME_PROXY.invoke(data.ns, data.params, data.callback, source, domain);
break;
}
}, false);
调用端iframe-proxy-client.js
var __sourceWin__ = null;
var __callbackMap__ = {};
window.addEventListener('message', function(event) {
var data = event.data;
switch (data.type) {
case 'IFRAME_PROXY_CALLBACK':
var fn = data.callback;
var isSuccess = data.code == 200;
if (isSuccess) {
__callbackMap__[fn].success(data.params);
} else {
__callbackMap__[fn].error(data.params);
}
break;
}
});
function ___noop() {};
var IFRAME_PROXY = {
invoke: function(ns, params) {
var callbackId = 'qnproxy' + new Date().getTime() + Math.floor(Math.random() * 100000) + Math.floor(Math.random() * 100000);
__callbackMap__[callbackId] = {
success: params.success || ___noop,
error: params.error || ___noop
}
for (var o in params) {
if (typeof params[o] == 'function') {
delete(params[o])
}
}
window.parent.postMessage({
type: 'IFRAME_PROXY',
ns: ns,
params: params,
callback: callbackId
}, '*')
}
}
如何使用
在iframe的的页面引入iframe-proxy-client.js
,在父页面引入 iframe-proxy.js
, 假设子页面需要调用父页面的方法, Util.getName('abc'),那么调用
IFRAME_PROXY.invoke('Util.getName','abc', function(res) {
//res就是父页面调用的函数结果
})
经过以上几步,就实现了一个通用了IFRAME通信库。