import { nanoid } from 'nanoid'; export type IframeMessageData = { action: string; id: string; data?: T; replyId?: string; errorCode?: number; //0为成功 message?: string; }; export type IframeMessageReq = { req: IframeMessageData; onReply: (data: IframeMessageData) => void; }; export class IframeException extends Error { code?: number = 0; constructor(data: IframeMessageData) { super(data.message); this.code = data.errorCode; } } export class IframeClient { requestQueue: Record = {}; //当前客户端是否是父级页面 iframe?: HTMLIFrameElement; onError?: any; handlers: Record) => Promise> = {}; constructor(iframe?: HTMLIFrameElement, onError?: (e: any) => void) { this.iframe = iframe; this.onError = onError; window.addEventListener('message', async (event: MessageEvent>) => { const data = event.data; if (data.action) { console.log(`收到消息[isSub:${this.isInFrame()}]`, data); try { const handler = this.handlers[data.action]; if (handler) { const res = await handler(data); if (data.id && data.action !== 'reply') { await this.send('reply', res, data.id); } } else { throw new Error(`action:${data.action} 未注册处理器,可能版本过低`); } } catch (e: any) { console.error(e); await this.send('reply', {}, data.id, 500, e.message); } } }); this.register('reply', async data => { const req = this.requestQueue[data.replyId!]; if (req) { req.onReply(data); delete this.requestQueue[data.replyId!]; } }); } isInFrame() { return window.self !== window.top; } register(action: string, handler: (data: IframeMessageData) => Promise) { this.handlers[action] = handler; } async send(action: string, data?: T, replyId?: string, errorCode?: number, message?: string): Promise> { try { return await this.doSend(action, data, replyId, errorCode, message); } catch (e) { if (this.onError) { this.onError(e); } throw e; } } async doSend(action: string, data?: T, replyId?: string, errorCode?: number, message?: string): Promise> { const reqMessageData: IframeMessageData = { id: nanoid(), action, data, replyId, errorCode, message, }; return new Promise((resolve, reject) => { const onReply = (reply: IframeMessageData) => { if (reply.errorCode && reply.errorCode > 0) { reject(new IframeException(reply)); return; } resolve(reply); }; this.requestQueue[reqMessageData.id] = { req: reqMessageData, onReply, }; try { console.log(`send message[isSub:${this.isInFrame()}]:`, reqMessageData); if (!this.iframe) { if (!window.parent) { reject('当前页面不在 iframe 中'); } window.parent.postMessage(reqMessageData, '*'); } else { //子页面 this.iframe.contentWindow?.postMessage(reqMessageData, '*'); } } catch (e) { console.error(e); reject(e); } }); } }