使用无界微前端框架嵌入页面

平台集成基于“WebComponent 容器 + iframe 沙箱”的无界微前端框架,加载第三方页面,实现平台及第三方系统隔离加载。

特别强调:

  • 使用平台开发的应用,不支持使用微前端嵌入
  • 移动端不支持使用微前端页面作为默认首页

过程如下:

  • 建一个微前端应用
  • 配置网关代理
  • 配置 serviceMetaInfo
  • 微前端页面改造,使用平台提供的事件和方法

新建应用

使用平台账号登录,打开“我的开发 -> 企业应用”,点击创建企业应用,以新增 SpringBootReact 空白应用为例。

图 0

配置网关代理

为了解决平台与第三方系统访问跨域问题,需要将第三方系统 url 在平台上配置网关(Kong)代理。在应用的 service 目录下新增目录,例如:test,并在创建目录下新增 app.m(固定名称不可修改) 文件,其中 name(建议同所在目录名)、request_path(请求访问路径)及 upstream_url(第三方系统目标 url)需要用户自定义,配置完成后重启控制台,使用“当前域名端口+配置请求路径”进行访问

app.m 代码如下:

<?xml version="1.0" encoding="UTF-8" ?>
<service>
    <!--名称,需要用户自定义,建议同所在目录名-->
    <name>test</name>
    <!--请求路径,需要用户自定义-->
    <request_path>/test</request_path>
    <preserve_host>true</preserve_host>
    <strip_request_path>true</strip_request_path>
    <!--目标url,需要用户自定义-->
    <upstream_url>http://192.168.0.76:8080</upstream_url>
    <plugin name="request-transformer" enabled="true">
        <add>
            <querystring  isarray="true">
                <__skin>newdao</__skin>
            </querystring>
        </add>
    </plugin>
    <!--跨域配置-->
    <plugin name="cors" enabled="true">
        <origins>*</origins>
        <credentials>true</credentials>
    </plugin>
</service>

配置微前端页面菜单

在创建应用 UI2/pcx 目录下新增 wujie.user.serviceMetaInfo.json 配置菜单,其中 url 需要使用上述配置的网关代理地址加参数 $openType=app,例如:

{
    "authorize":{
        "permissions":[{
            "code":"*:http://reactdevguide8-lzsdev-ide.trunk2.xcaas.com:8800/test/index.html?$openType=app:get",
            "id":"wujie",
            "name":"wujie",
            "type":"menu",
            "ui":false
        }],
        "roles":[{
            "code":"{serviceName}_integrated_wujie",
            "id":"integrated_wujie",
            "name":"{serviceLabel}_微前端_功能角色",
            "permissions":["*:http://reactdevguide8-lzsdev-ide.trunk2.xcaas.com:8800/test/index.html?$openType=app:get"]
        }]
    },
    "menu":{
        "children":[
            {
                "children":[
                    {
                        "children":[],
                        "ext":{},
                        "title":"微前端",
                        "types":["pcx","func"],
                        "url":"http://reactdevguide8-lzsdev-ide.trunk2.xcaas.com:8800/test/index.html?$openType=app"
                    }
                ],
                "ext":{},
                "title":"集成",
                "types":[]
            }
        ],
        "ext":{},
        "title":"{serviceLabel}",
        "types":[]
    },
    "serviceInfo":{
        "label":"{serviceLabel}",
        "name":"{serviceName}"
    }
}

配置完成之后发布应用并到企业门户“系统管理 -> 服务注册”进行服务刷新,即可在“功能树配置”看到新增的微服务菜单,按需进行菜单权限配置。

图 1

微前端页面改造

在第三方系统开发过程中,可以引入 shell.js 文件,用于降级开发使用,通过 window.Shell 获取兼容事件及方法。

/**
 * shell.js
 * 当前js为用来脱离门户环境测试使用 正常生产环境可以不引入 保留下来会自动忽略
 */

const Shell = {
  /**
   *  生命周期 接入
   *  onCreate 应该在这里 做react的 mount
   *  onDestroy 应该在这里 做react的 unmount
   *  onShow 门户中页面激活时候
   *  onHide 门户中页面隐藏时候
   */
  onShow: function (callback) {
    callback();
  },
  onHide: function (callback) {
  },
  onCreate: function (callback) {
    callback();
  },
  onDestroy: function (callback) {
  },

  /**
   * 路由行为 api
   *  一般用来关闭自己
   *  如果需要打开一个其他页面关闭自己
   *
   *  navigateBack, navigateTo, preload, refresh, close, closeOther, closeAll
   *
   * if (window && window.parent && window.parent.postMessage){
   *           let msg = {type: "shell", name: "navigateBack", args: {}};
   *           if (currentPage){
   *               msg.args.pageUrl = currentPage.pageUrl;
   *           }
   *           window.parent.postMessage(JSON.stringify(msg), '*');
   *       }
   *
   */
  navigateTo(params) {
    window.open(params.url);
  },

  navigateBack() {
    window.history.back();
  },

  refresh() {
    window.location.reload()
  },

  /**
   *  共享变量  主要用来存储跨页面的共享变量
   *  主要存储 共享的用户信息  共享的基础js库比如 window.lodash 等
   */
  async getShardState(path,loader) {
    let result = await loader?.();
    return result;
  }
}
window.Shell = window.Shell || Shell;

使用平台提供的页面事件

包含:onCreate(页面创建)、onDestroy(页面销毁)、onShow(页面激活)及 onHide(页面隐藏),其中 onShow 及 onHide 事件在移动端不兼容,4个页面事件在使用时仅需传回调方法即可。

调用平台提供的方法

包含:getShardState、navigateBack、navigateTo 及 refresh,其中移动端不支持 refresh,getShardState 方法仅在微前端页面中有效。 详细方法说明:

  • navigateBack:关闭当前页面,无参数

例如:

window.Shell.navigateBack();
  • navigateTo:打开指定页面,参数与平台使用方法参数一致,常用参数包含:
    • url:打开地址
    • params:打开地址参数

例如:

let pms={url:"/entry/opm-mobileapp/opm-mobile/userVisited",params:{title:"用户使用情况2"}};
window.Shell.navigateTo(pms);
  • refresh:刷新当前页面,无参数

例如:

window.Shell.refresh();
  • getShardState:共享变量,主要用来存储跨页面的共享变量,相同 path 数据如果有值则直接返回,没有则新增,参数包含:
    • path:设置或获取数据 key
    • loader:请求数据函数(支持异步)

例如:

let result=await window.Shell.getShardState("wujie.userInfo",async()=>{
    console.log("没有1 amInfo信息,第一次访问主动登录");
    let userInfo= await new Promise((resolve,reiect)=>{
        setTimeout(()=>{
            console.log("模拟登录5s后登录成功返回登录信息");
            resolve({
                userName:"张三",
                userId:"123"
            })
        },3000)
    });
    return userInfo;
});
pageLog(JSON.stringify(result),"共享数据");

使用案例

完整案例

图 2

<html>
    <head>
        <meta charset="utf-8"/>
        <script src="./shell.js"></script>
        <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
        <style>
            body{padding:20px;}
            a{margin:5px 10px;padding:6px 10px;border:1px solid #ccc;border-radius: 5px;text-decoration: none;float: left;}
            .btns,.log{overflow: hidden;}
            .log{max-height: 500px;overflow: auto;padding: 15px;}
        </style>
    </head>
    <body>
        <div class="btns">
            <a type="refresh" isMobile="false">刷新</a>  
            <a type="navigateTo" isMobile="true">打开页面</a>
            <a type="navigateBack" isMobile="true">关闭当前页面</a>   
            <a type="getShardState" isMobile="true">获取用户信息</a>
        </div>
        <div class="log"></div>
        <script type="text/javascript"> 
            const pageLog=(log,type)=>{
                let text=type+":"+log;
                let logDiv=$("<p>",{text});
                $(".log").append(logDiv);
                console.debug(text);
            }
            //判断是否为微前端
            pageLog(`当前页面${window.$wujie?"是":"不是"}微前端`,"微前端判断");

            //控制按钮显示
            let {isPcx}=window.parent;
            if(!isPcx){
                $("a[isMobile='false']").hide();
            }  

            //页面事件              
            window.Shell.onCreate(()=>pageLog("创建页面","页面事件"));
            window.Shell.onDestroy(()=>pageLog("销毁页面","页面事件"));
            //页面激活及隐藏事件在移动端无效
            window.Shell.onShow(()=>pageLog("激活页面","页面事件"));
            window.Shell.onHide(()=>pageLog("隐藏页面","页面事件"));
            /**
             * 系统兼容方法(兼容无界及iframe)
             * 包含:navigateBack, navigateTo, refresh, getShardState(iframe无效)
             *      移动端不支持:refresh
             * 详细说明:
             *      navigateBack:关闭当前页面,无参数
             *      navigateTo:打开指定页面,参数与平台使用方法参数一致,常用参数包含url,params
             *              例如:{url:"/react/mobileapp/mobile/dbrest/operationLogs",params:{title:"数据日志2"}}
             *      refresh:刷新当前页面,无参数   
             *      getShardState:共享变量,主要用来存储跨页面的共享变量,相同 path 数据如果有值则直接返回,没有则新增,参数包含:
             *          path:设置或获取数据 key  
             *          loader:请求数据函数(支持异步)
             */  
            $("a").on("click",async(e)=>{
                e.preventDefault();  
                let type=$(e.currentTarget).attr("type");      
                console.log(window.Shell)
                switch(type){
                    case "refresh":window.Shell.refresh();break;
                    case "navigateTo":
                        let pms={url:"/entry/opm-mobileapp/opm-mobile/userVisited",params:{title:"用户使用情况2"}};
                        if(!isPcx){
                            //移动端页面
                            pms.url="/entry/mobileapp/mobile/task";
                            delete pms.params;             
                        }
                        window.Shell.navigateTo(pms);
                        break;
                    case "navigateBack":window.Shell.navigateBack();break;
                    case "getShardState":
                        let result=await window.Shell.getShardState("wujie.userInfo",async()=>{
                            pageLog("没有wujie.userInfo信息,第一次访问主动登录","共享数据");
                            let userInfo= await new Promise((resolve,reiect)=>{
                                setTimeout(()=>{
                                    pageLog("模拟登录5s后登录成功返回登录信息","共享数据");
                                    resolve({
                                        userName:"张三",
                                        userId:"123"
                                    })
                                },3000)
                            });
                            return userInfo;
                        });
                        pageLog(JSON.stringify(result),"共享数据");
                        break;
                }
            })
        </script>
    </body>
</html>

获取当前是否是微前端

通过 window.$wujie 是否有数据来判断是否为微前端

if(window.$wujie){
    console.log("当前是微前端页面")
}else{
    console.log("当前不是微前端页面")
}

results matching ""

    No results matching ""