Files
certd/packages/libs/lib-iframe/src/lib/iframe.client.ts

123 lines
3.4 KiB
TypeScript
Raw Normal View History

2024-09-20 15:15:24 +08:00
import { nanoid } from 'nanoid';
2024-09-22 02:06:34 +08:00
export type IframeMessageData<T> = {
2024-09-20 15:15:24 +08:00
action: string;
id: string;
2024-09-22 02:06:34 +08:00
data?: T;
2024-09-20 15:15:24 +08:00
replyId?: string;
2024-09-22 02:06:34 +08:00
errorCode?: number; //0为成功
message?: string;
2024-09-20 15:15:24 +08:00
};
2024-09-22 02:06:34 +08:00
export type IframeMessageReq<T = any, R = any> = {
req: IframeMessageData<T>;
onReply: (data: IframeMessageData<R>) => void;
2024-09-20 15:15:24 +08:00
};
2024-09-22 02:06:34 +08:00
export class IframeException extends Error {
code?: number = 0;
constructor(data: IframeMessageData<any>) {
super(data.message);
this.code = data.errorCode;
}
}
2024-09-20 15:15:24 +08:00
export class IframeClient {
2024-09-22 02:06:34 +08:00
requestQueue: Record<string, IframeMessageReq> = {};
2024-09-20 15:15:24 +08:00
//当前客户端是否是父级页面
iframe?: HTMLIFrameElement;
2024-09-23 14:04:33 +08:00
onError?: any;
2024-09-22 02:06:34 +08:00
handlers: Record<string, (data: IframeMessageData<any>) => Promise<void>> = {};
2024-09-23 14:04:33 +08:00
constructor(iframe?: HTMLIFrameElement, onError?: (e: any) => void) {
2024-09-20 15:15:24 +08:00
this.iframe = iframe;
2024-09-23 14:04:33 +08:00
this.onError = onError;
2024-09-22 02:06:34 +08:00
window.addEventListener('message', async (event: MessageEvent<IframeMessageData<any>>) => {
2024-09-20 15:15:24 +08:00
const data = event.data;
2024-09-22 02:06:34 +08:00
if (data.action) {
2024-09-23 13:23:49 +08:00
console.log(`收到消息[isSub:${this.isInFrame()}]`, data);
2024-09-22 02:06:34 +08:00
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 {
2024-09-23 14:04:33 +08:00
throw new Error(`action:${data.action} 未注册处理器,可能版本过低`);
2024-09-22 02:06:34 +08:00
}
} catch (e: any) {
2024-09-23 13:23:49 +08:00
console.error(e);
2024-09-22 02:06:34 +08:00
await this.send('reply', {}, data.id, 500, e.message);
2024-09-20 15:15:24 +08:00
}
}
});
2024-09-22 02:06:34 +08:00
this.register('reply', async data => {
const req = this.requestQueue[data.replyId!];
if (req) {
req.onReply(data);
delete this.requestQueue[data.replyId!];
}
});
2024-09-20 15:15:24 +08:00
}
isInFrame() {
return window.self !== window.top;
}
2025-04-19 11:48:23 +08:00
register<T = any>(action: string, handler: (data: IframeMessageData<T>) => Promise<any>) {
2024-09-22 02:06:34 +08:00
this.handlers[action] = handler;
}
async send<R = any, T = any>(action: string, data?: T, replyId?: string, errorCode?: number, message?: string): Promise<IframeMessageData<R>> {
2024-09-23 14:04:33 +08:00
try {
return await this.doSend<R, T>(action, data, replyId, errorCode, message);
} catch (e) {
if (this.onError) {
this.onError(e);
}
throw e;
}
}
async doSend<R = any, T = any>(action: string, data?: T, replyId?: string, errorCode?: number, message?: string): Promise<IframeMessageData<R>> {
2024-09-22 02:06:34 +08:00
const reqMessageData: IframeMessageData<T> = {
2024-09-20 15:15:24 +08:00
id: nanoid(),
action,
data,
replyId,
2024-09-22 02:06:34 +08:00
errorCode,
message,
2024-09-20 15:15:24 +08:00
};
return new Promise((resolve, reject) => {
2024-09-23 13:23:49 +08:00
const onReply = (reply: IframeMessageData<R>) => {
2024-09-22 02:06:34 +08:00
if (reply.errorCode && reply.errorCode > 0) {
reject(new IframeException(reply));
return;
}
2024-09-20 15:15:24 +08:00
resolve(reply);
};
2024-09-22 02:06:34 +08:00
this.requestQueue[reqMessageData.id] = {
2024-09-20 15:15:24 +08:00
req: reqMessageData,
onReply,
};
try {
2024-09-23 13:23:49 +08:00
console.log(`send message[isSub:${this.isInFrame()}]:`, reqMessageData);
2024-09-20 15:15:24 +08:00
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);
}
});
}
}