Skip to main content
Version: 1.x

子应用改造

改造思路

  1. 页面初始化,从URL可以拿到isInIframe和page等参数,这个参数是Godzilla加载子应用的时候会提供的,如果是以子应用的被加载,则这个参数为true
  2. 拿到isInIframe这个参数后,子应用就可以根据这个参数做相应的布局调整,一般常见系统的布局如下,分为头部、底部、左侧菜单栏和右侧内容区,但我们作为一个子应用的时候,应该只需要内容区,其它都不需要显示。如果是以路由为基础的子应用(例如采用Angular、React、Vue开发的框架),那么只需要渲染路由的那一部分,其它都不需要渲染。
  3. 如果作为子系统,那么页面的加载和切换就不能够通过用户点击来操作了,因为页面的加载和切换是由主系统(portal)来控制的,在这种情况下,只能是主系统通知子系统要加载页面,子系统收到指令后,自己调用API来控制页面,目前可以采用postMessage这种技术来实现主系统和子系统之间的通信
  4. 子系统监听主系统的消息指令,并做相应的处理
    1. 子系统完成初始化,向主系统发送iframeReady事件,告知子系统已经完成初始化
    2. 子系统接收openPage事件,根据page参数利用api打开相应的页面
    3. 子系统接收closePage事件,根据page参数利用api关闭相应的页面
    4. 子系统接收reloadPage事件,根据page参数利用api重新加载相应的页面

PNG

接入 Iframe 子应用

第一步,portal 应用在打开子应用的时候,会将认证信息传递过去,子应用完成自身认证

假设在【应用接入】填写的应用地址http://sample.com:8080/login 应用 IDsample,那么 portal 应用在打开子应用的时候,会将 authToken、isIframe 等信息传递过去,例如http://sample.com:8080/login?authToken=xxx&&appId=xxx&&page=xxx&&isIframe=true&&theme=xx,各个参数含义如下

  • authToken,必传,认证的 jwt 信息,在开发环境下私钥是quantex
  • appId,必传,这个就是在【应用接入】那里填写的应用 ID,这里暂时是冗余信息
  • page,必传,这个是子应用的页面地址(配置在菜单上),这里暂时是冗余信息,可以不必理会,在页面加载完之后,portal 应用还会通过 postMessage 告之这个页面地址
  • isIframe, 参数值为 true,子应用可以根据这个参数来识别
  • theme, 必传,第一次打开页面的主题,后续主题切换会通过 postMessage 方式

第二步,portal 应用统一管理子应用菜单,子应用将菜单维护到 portal 应用

打开【菜单管理】(如果是静态菜单,则修改bff相应文件),将子应用的菜单信息添加进去,注意里面的应用 ID 就是上文提到,需要对应起来

第三步,子应用需进行一系列改造,在嵌入到 portal 应用时,只保留子应用的页面信息,其它信息需要隐藏(例如左侧的菜单信息、顶部的导航栏信息、底部的状态栏信息),portal 应用和子应用有如下交互

  • 子应用主页加载完后,需要向 portal 应用发送 iframeReady 信息,通知子应用已经加载完毕,portal 应用可以向子应用发送消息(通过 postMessage 的方式)

    if (window.isInIframe) {
    // NOTE: 当项目使用 iframe 模式打开时, 通知父窗口, iframe 加载完成
    window.parent.postMessage(
    {
    type: 'iframeReady', // 消息类型
    appId: 'sample' // 这个appId就是上文提到的应用Id,必须提供
    },
    '*'
    );
    }
  • 子应用收到 closePage 时, 可以向 portal 发起通知阻止关闭对应页面的 tab

    // 省略不相关代码
    window.addEventListener('message', e => {
    const { type } = e.data;
    switch (type) {
    case 'closePage':
    notifyClosePages(e.data);
    break;
    // other logic you need
    }
    });

    function notifyClosePages({ page }) {
    // portal收到消息后会阻止关闭 appId page 同时匹配的 tab
    // appId 和 page 都必须有值, 否则将忽略此次消息通知
    window.parent.postMessage(
    {
    type: 'stopClosePage',
    appId: process.env.APP_ID, // 必传参数
    page // 必传参数
    },
    '*'
    );
    // 收到通知后子系统可以需要发起弹窗通知, 以下仅为示例, 具体实现子应用自行考虑
    // 重要的一点: portal 在收到消息后对 tab 处理, 可能会发生页面更新, 为了避免更新时与弹窗通知发生冲突, 弹窗等反馈操作需要作延时处理
    setTimeout(() => {
    if (window.confirm(`确认关闭${process.env.APP_ID}吗?`)) {
    window.parent.postMessage(
    {
    type: 'closePage',
    appId: process.env.APP_ID,
    page
    },
    '*'
    );
    }
    }, 200);
    }
  • portal 应用通知子应用打开指定页面 openPage,子应用接收到 openPage 信息后,拿到 page 信息,打开相关页面

    window.postMessageToIframe({
    appId: 'sample',
    page: 'sample/User', // 这个就是在菜单管理那里填入的菜单URL
    type: 'openPage' // 消息类型
    });
  • portal 应用通知子应用重新指定页面,子应用接收到 reloadPage 信息后,拿到 page 信息,重新加载相关页面

    window.postMessageToIframe({
    appId: 'sample',
    type: 'reloadPage',
    page: 'sample/User'
    });
  • portal 应用通知子应用关闭指定页面,子应用接收到 closePage 信息后,拿到 page 信息,关闭相关页面

    window.postMessageToIframe({
    appId: 'sample',
    type: 'closePage',
    page: 'sample/User'
    });
  • portal 应用通知子应用切换主题 switchTheme,子应用接收到 switchTheme 信息后,拿到 theme 信息,切换主题

    window.postMessageToIframe({
    appId: 'sample',
    type: 'switchTheme',
    theme: getCurrentTheme()
    });