与跨域 iframe 通信示例
window.addEventListener('load', function(){
var iframes = document.getElementsByTagName('iframe');
for (var i = 0; i < iframes.length; i++) {
var iframe = iframes[i];
var bridge = new IframeBridge(iframe);
iframe.bridge = bridge;
}
});
class IframeBridge {
constructor(iframe, targetOrigin = '*') {
this.iframe = iframe;
this.targetOrigin = targetOrigin;
this.pendingRequests = new Map(); // 存储进行中的请求
this.setupMessageListener();
}
setupMessageListener() {
window.addEventListener('message', (event) => {
// 安全检查:验证来源[1,3](@ref)
if (this.targetOrigin !== '*' && event.origin !== this.targetOrigin) {
return;
}
const message = event.data;
// 检查是否为响应消息,并且有对应的请求ID
if (message?._type === 'RESPONSE' && message?.requestId) {
const pendingRequest = this.pendingRequests.get(message.requestId);
if (pendingRequest) {
// 从等待列表中移除
this.pendingRequests.delete(message.requestId);
if (message.success) {
// 成功则解析Promise
pendingRequest.resolve(message.data);
} else {
// 失败则拒绝Promise
pendingRequest.reject(new Error(message.error || 'Request failed'));
}
}
}
});
}
async request(action, data = {}, timeout = 5000) {
// 生成唯一请求ID[7](@ref)
const requestId = this.generateRequestId();
return new Promise((resolve, reject) => {
// 设置超时
const timeoutId = setTimeout(() => {
this.pendingRequests.delete(requestId);
reject(new Error('Request timeout'));
}, timeout);
// 存储resolve和reject以便后续使用
this.pendingRequests.set(requestId, {
resolve: (responseData) => {
clearTimeout(timeoutId);
resolve(responseData);
},
reject: (error) => {
clearTimeout(timeoutId);
reject(error);
}
});
// 向iframe发送消息[2,5](@ref)
this.iframe.contentWindow.postMessage({
_type: 'REQUEST',
requestId,
action,
data
}, this.targetOrigin);
});
}
generateRequestId() {
return `${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
}
}
// 使用async/await进行通信
async function fetchDataFromIframe() {
var iframes = document.getElementsByTagName('iframe');
for (var i = 0; i < iframes.length; i++) {
var iframe = iframes[i];
var bridge = iframe.bridge;
if(!bridge)
continue;
try {
console.log('请求用户数据...');
// 代码会在此"等待"直到收到响应
const userData = await bridge.request('GET_USER_DATA', { userId: 123 });
console.log('收到数据:', userData);
// 继续执行后续逻辑
const settings = await bridge.request('GET_USER_SETTINGS');
console.log('用户设置:', settings);
} catch (error) {
console.error('通信失败:', error.message);
}
}
}
window.addEventListener('message', async (event) => {
const message = event.data;
// 处理请求类型的消息
if (message?._type === 'REQUEST') {
let responseData;
let success = true;
try {
// 根据不同的action执行相应操作
switch (message.action) {
case 'GET_USER_DATA':
//responseData = await getUserData(message.data.userId);
responseData = "I am user " + message.data.userId;
break;
case 'GET_USER_SETTINGS':
//responseData = await getUserSettings();
responseData = "I am UserSettingsxxxx";
break;
default:
throw new Error(`未知操作: ${message.action}`);
}
} catch (error) {
responseData = { error: error.message };
success = false;
}
// 回复消息,带回请求ID[3](@ref)
event.source.postMessage({
_type: 'RESPONSE',
requestId: message.requestId, // 必须带回原请求ID
success,
data: responseData
}, event.origin);
}
});
桂棹兮兰桨,击空明兮溯流光。
浙公网安备 33010602011771号