micro-iframe
Version:
a communication library for iframe-based micro-frontend integration
2 lines (1 loc) • 10.3 kB
JavaScript
"use strict";var e=require("mitt");class t{fromApp;targetApp;type;data;static MESSAGE_TYPE_PREFIX="MICRO_IFRAME_";constructor({fromApp:e,targetApp:t,type:I,data:_}){this.fromApp=e,this.targetApp=t,this.type=I,this.data=_}static isValid(e){return e&&"object"==typeof e&&"string"==typeof e.fromApp&&"string"==typeof e.targetApp&&"string"==typeof e.type&&"data"in e&&e.type&&e.type.startsWith(t.MESSAGE_TYPE_PREFIX)}static getRealMessage(e){return e.type=e.type.replace(t.MESSAGE_TYPE_PREFIX,""),e}static getRealMessageType(e){return e.replace(t.MESSAGE_TYPE_PREFIX,"")}}const I=e();function _(e){const t=[];try{return function e(I,_){const{name:i,iframeId:M,origin:R}=I,E=[..._,i];t.push({name:i,iframeId:M,origin:R,keyPath:E,children:I?.children?.map((e=>e.name))||[]}),I.children&&I.children.length>0&&I.children.forEach((t=>e(t,E)))}(e,[]),t}catch(e){throw console.error("[micro-iframe] flattenTreeWithKeyPath error:",e),new Error("[micro-iframe] 路由表数据结构不正确,解析失败,请检查!!!")}}class i{MICRO_IFRAME_ORIGIN_ROUTES={};MICRO_IFRAME_ROUTES=[];MICRO_IFRAME_CUR_ROUTE={};MICRO_IFRAME_NAME="";MICRO_IFRAME_OPTIONS={log:!1};static MICRO_IFRAME_EVENT={INIT_RESPONSE_CONFIG:`${t.MESSAGE_TYPE_PREFIX}INIT_RESPONSE_CONFIG`,INIT_REQUEST_CONFIG:`${t.MESSAGE_TYPE_PREFIX}INIT_REQUEST_CONFIG`};constructor(e,t,I){this.MICRO_IFRAME_NAME=e,this.MICRO_IFRAME_OPTIONS=Object.assign({},this.MICRO_IFRAME_OPTIONS,I);const i=_(t),M=i.find((t=>t.name===e));if(!M)throw new Error(`[micro-iframe]-[${this.MICRO_IFRAME_NAME}]应用: ${e} 在路由表中未找到匹配项, 请检查注册应用名称和路由表是否匹配`);this.MICRO_IFRAME_ORIGIN_ROUTES=t,this.MICRO_IFRAME_ROUTES=i,this.MICRO_IFRAME_CUR_ROUTE=M,console.log(`[micro-iframe] 主应用: ${this.MICRO_IFRAME_NAME}注册成功`),this.__init()}__handleIframeInitRequestConfigMessage(e){const I=document.getElementById(e.iframeId)?.contentWindow;if(I){const _=new t({fromApp:this.MICRO_IFRAME_NAME,targetApp:e.name,type:i.MICRO_IFRAME_EVENT.INIT_RESPONSE_CONFIG,data:this.MICRO_IFRAME_ORIGIN_ROUTES});I.postMessage(_,e.origin)}}__handleIframeForwardMessage(e){const t=this.MICRO_IFRAME_ROUTES.find((t=>t.name===e.targetApp));if(!t)return void(this.MICRO_IFRAME_OPTIONS.log&&console.error(`[micro-iframe]-[${this.MICRO_IFRAME_NAME}]应用: 转发消息, 消息目标应用${e.targetApp}未在路由表中注册,发送失败`));const I=t.keyPath.findIndex((e=>e===this.MICRO_IFRAME_NAME));if(-1===I||I+1>=t.keyPath.length)return void(this.MICRO_IFRAME_OPTIONS.log&&console.error(`[micro-iframe]-[${this.MICRO_IFRAME_NAME}]应用: 转发消息, 消息中间应用节点未找到,发送失败`));const _=t.keyPath[I+1],i=this.MICRO_IFRAME_ROUTES.find((e=>e.name===_));if(!i)return void(this.MICRO_IFRAME_OPTIONS.log&&console.error(`[micro-iframe]-[${this.MICRO_IFRAME_NAME}]应用: 转发消息, 消息中间应用${_}未在路由表中注册,发送失败`));const M=document.getElementById(i.iframeId||"")?.contentWindow;M?M.postMessage(e,i.origin):this.MICRO_IFRAME_OPTIONS.log&&console.error(`[micro-iframe]-[${this.MICRO_IFRAME_NAME}]应用: 转发消息, 消息中间应用目标窗口: ${i.iframeId}未找到, 发送失败`)}__checkMessageIsValid(e){if(!t.isValid(e?.data||{}))return void(this.MICRO_IFRAME_OPTIONS.log&&console.warn(`[micro-iframe]-[${this.MICRO_IFRAME_NAME}]应用: 接收到消息不符合micro-iframe规则, 该消息被丢弃`,e));return!!this.MICRO_IFRAME_ROUTES.find((t=>t.name===e.data.fromApp))||(this.MICRO_IFRAME_OPTIONS.log&&console.warn(`[micro-iframe]-[${this.MICRO_IFRAME_NAME}]应用: 接收到未在路由表注册消息,该消息被丢弃`,e),!1)}__init(){window.addEventListener("message",(e=>{if(this.MICRO_IFRAME_OPTIONS.log&&console.log(`[micro-iframe]-[${this.MICRO_IFRAME_NAME}]应用接收到消息:`,e),!this.__checkMessageIsValid(e))return;const _=new t(e?.data||{});if(_.type!==i.MICRO_IFRAME_EVENT.INIT_REQUEST_CONFIG)_.targetApp===this.MICRO_IFRAME_NAME?I.emit(t.getRealMessageType(_.type),t.getRealMessage(_)):this.__handleIframeForwardMessage(_);else{const t=this.MICRO_IFRAME_ROUTES.find((t=>t.name===e.data.fromApp));this.__handleIframeInitRequestConfigMessage(t)}}))}$on(e,t){I.on(e,t)}$emit(e,I,_){const i=this.MICRO_IFRAME_ROUTES.find((t=>t.name===e));if(i)if(this.MICRO_IFRAME_CUR_ROUTE.children.includes(e)){const M=new t({fromApp:this.MICRO_IFRAME_NAME,targetApp:e,type:`${t.MESSAGE_TYPE_PREFIX}${I}`,data:_}),R=document.getElementById(i.iframeId)?.contentWindow;R?R.postMessage(M,i.origin):this.MICRO_IFRAME_OPTIONS.log&&console.error(`[micro-iframe]-[${this.MICRO_IFRAME_NAME}]应用: 发送消息, 目标应用${e}的窗口${i.iframeId}未找到, 发送失败`)}else this.__handleIframeForwardMessage(new t({fromApp:this.MICRO_IFRAME_NAME,targetApp:e,type:`${t.MESSAGE_TYPE_PREFIX}${I}`,data:_}));else this.MICRO_IFRAME_OPTIONS.log&&console.error(`[micro-iframe]-[${this.MICRO_IFRAME_NAME}]应用: 发送消息, 目应用${e}在路由表中未注册, 发送失败`)}}class M{MICRO_IFRAME_ORIGIN_ROUTES={};MICRO_IFRAME_ROUTES=[];MICRO_IFRAME_CUR_ROUTE={};MICRO_IFRAME_IS_HANDSHAKE=!1;MICRO_IFRAME_NAME="";POWERED_BY_MICRO_IFRAME=!1;MICRO_IFRAME_OPTIONS={log:!1};static MICRO_IFRAME_EVENT={INIT_RESPONSE_CONFIG:`${t.MESSAGE_TYPE_PREFIX}INIT_RESPONSE_CONFIG`,INIT_REQUEST_CONFIG:`${t.MESSAGE_TYPE_PREFIX}INIT_REQUEST_CONFIG`};constructor(e,t){this.__init(e,t)}__handleIframeInitResponseConfigMessage(e){if(this.MICRO_IFRAME_IS_HANDSHAKE)return;const t=_(e.data),i=t.find((e=>e.name===this.MICRO_IFRAME_NAME));i?(this.MICRO_IFRAME_ORIGIN_ROUTES=e.data,this.MICRO_IFRAME_ROUTES=t,this.MICRO_IFRAME_CUR_ROUTE=i,this.POWERED_BY_MICRO_IFRAME=!0,this.MICRO_IFRAME_IS_HANDSHAKE=!0,console.log(`[micro-iframe] 子应用: ${this.MICRO_IFRAME_NAME}注册成功`),I.emit("iframe-ready",e)):this.MICRO_IFRAME_OPTIONS.log&&console.error(`[micro-iframe]-[${this.MICRO_IFRAME_NAME}]应用: ${this.MICRO_IFRAME_NAME} 在路由表中未找到匹配项, 请检查注册应用名称和路由表是否匹配`)}__handleIframeInitRequestConfigMessage(e){const I=document.getElementById(e.iframeId)?.contentWindow;if(I){const _=new t({fromApp:this.MICRO_IFRAME_NAME,targetApp:e.name,type:M.MICRO_IFRAME_EVENT.INIT_RESPONSE_CONFIG,data:this.MICRO_IFRAME_ORIGIN_ROUTES});I.postMessage(_,e.origin)}}__handleIframeForwardMessage(e){const t=this.MICRO_IFRAME_ROUTES.find((t=>t.name===e.targetApp));if(t)if(t.keyPath.includes(this.MICRO_IFRAME_NAME)){const I=t.keyPath.findIndex((e=>e===this.MICRO_IFRAME_NAME));if(-1===I||I+1>=t.keyPath.length)return void(this.MICRO_IFRAME_OPTIONS.log&&console.error(`[micro-iframe]-[${this.MICRO_IFRAME_NAME}]应用: 转发消息, 消息中间应用节点未找到,发送失败`));const _=t.keyPath[I+1],i=this.MICRO_IFRAME_ROUTES.find((e=>e.name===_));if(!i)return void(this.MICRO_IFRAME_OPTIONS.log&&console.error(`[micro-iframe]-[${this.MICRO_IFRAME_NAME}]应用: 发送消息, 消息中间应用${_}未在路由表中注册, 发送失败`));const M=document.getElementById(i.iframeId)?.contentWindow;M?M.postMessage(e,i.origin):this.MICRO_IFRAME_OPTIONS.log&&console.error(`[micro-iframe]-[${this.MICRO_IFRAME_NAME}]应用: 发送消息, 目标应用${_}的窗口${t.iframeId}未找到, 发送失败`)}else{const I=this.__getTargetWindowFromKeyPath(t);I?.window?I?.window?.postMessage(e,I.hitMainApp?this.MICRO_IFRAME_ORIGIN_ROUTES.origin:t.origin):this.MICRO_IFRAME_OPTIONS.log&&console.error(`[micro-iframe]-[${this.MICRO_IFRAME_NAME}]应用: 发送消息, 目标应用${t}的窗口${t.iframeId}未找到, 发送失败`)}else this.MICRO_IFRAME_OPTIONS.log&&console.error(`[micro-iframe]-[${this.MICRO_IFRAME_NAME}]应用: 转发消息, 消息目标应用${e.targetApp}未在路由表中注册,发送失败`)}__getTargetWindowFromKeyPath(e){let t=null,I=!1;const _=Object.assign(this.MICRO_IFRAME_CUR_ROUTE.keyPath);for(let i=_.length-1;i>=0;i--)if(_[i]!==this.MICRO_IFRAME_NAME){if(_[i]===this.MICRO_IFRAME_ORIGIN_ROUTES.name){t=t?.parent,I=!0;break}if(_[i]===e.name){t=t?.parent;break}t=t?.parent}else t=window;return{window:t,hitMainApp:I}}__checkMessageIsValid(e){if(!t.isValid(e?.data||{}))return void(this.MICRO_IFRAME_OPTIONS.log&&console.warn(`[micro-iframe]-[${this.MICRO_IFRAME_NAME}]应用: 接收到消息不符合micro-iframe规则, 该消息被丢弃`,e));return!!this.MICRO_IFRAME_ROUTES.find((t=>t.name===e.data.fromApp))||(this.MICRO_IFRAME_OPTIONS.log&&console.warn(`[micro-iframe]-[${this.MICRO_IFRAME_NAME}]应用: 接收到未在路由表注册消息,该消息被丢弃`,e),!1)}__init(e,_){this.MICRO_IFRAME_NAME=e,this.MICRO_IFRAME_OPTIONS=Object.assign({},this.MICRO_IFRAME_OPTIONS,_);const i=new t({fromApp:this.MICRO_IFRAME_NAME,targetApp:"",type:M.MICRO_IFRAME_EVENT.INIT_REQUEST_CONFIG,data:void 0});window?.parent.postMessage(i,"*"),window.addEventListener("message",(e=>{if(this.MICRO_IFRAME_OPTIONS.log&&console.log(`[micro-iframe]-[${this.MICRO_IFRAME_NAME}]应用接收到消息:`,e),e?.data?.type===M.MICRO_IFRAME_EVENT.INIT_RESPONSE_CONFIG)return void this.__handleIframeInitResponseConfigMessage(e?.data||{});if(!this.__checkMessageIsValid(e))return;const _=new t(e?.data||{});if(_.type!==M.MICRO_IFRAME_EVENT.INIT_REQUEST_CONFIG)_.targetApp===this.MICRO_IFRAME_NAME?I.emit(t.getRealMessageType(_.type),t.getRealMessage(_)):this.__handleIframeForwardMessage(_);else{const t=this.MICRO_IFRAME_ROUTES.find((t=>t.name===e.data.fromApp));this.__handleIframeInitRequestConfigMessage(t)}}))}$on(e,t){I.on(e,t)}$emit(e,I,_){const i=this.MICRO_IFRAME_ROUTES.find((t=>t.name===e));if(i)if(this.MICRO_IFRAME_CUR_ROUTE.children.includes(e)){const M=new t({fromApp:this.MICRO_IFRAME_NAME,targetApp:e,type:`${t.MESSAGE_TYPE_PREFIX}${I}`,data:_}),R=document.getElementById(i.iframeId)?.contentWindow;R?R.postMessage(M,i.origin):this.MICRO_IFRAME_OPTIONS.log&&console.error(`[micro-iframe]-[${this.MICRO_IFRAME_NAME}]应用: 发送消息, 目标应用${e}的窗口${i.iframeId}未找到, 发送失败`)}else this.__handleIframeForwardMessage(new t({fromApp:this.MICRO_IFRAME_NAME,targetApp:e,type:`${t.MESSAGE_TYPE_PREFIX}${I}`,data:_}));else this.MICRO_IFRAME_OPTIONS.log&&console.error(`[micro-iframe]-[${this.MICRO_IFRAME_NAME}]应用: 发送消息, 目应用${e}在路由表中未注册, 发送失败`)}}exports.MicroChildApp=M,exports.MicroMainApp=i;