与跨域 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);
    }
});

 

posted on 2025-11-08 13:57  空明流光  阅读(0)  评论(0)    收藏  举报

导航