使用无界微前端框架嵌入页面
平台集成基于“WebComponent 容器 + iframe 沙箱”的无界微前端框架,加载第三方页面,实现平台及第三方系统隔离加载。
特别强调:
- 使用平台开发的应用,不支持使用微前端嵌入
- 移动端不支持使用微前端页面作为默认首页
过程如下:
- 建一个微前端应用
- 配置网关代理
- 配置 serviceMetaInfo
- 微前端页面改造,使用平台提供的事件和方法
新建应用
使用平台账号登录,打开“我的开发 -> 企业应用”,点击创建企业应用,以新增 SpringBootReact 空白应用为例。
配置网关代理
为了解决平台与第三方系统访问跨域问题,需要将第三方系统 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}"
}
}
配置完成之后发布应用并到企业门户“系统管理 -> 服务注册”进行服务刷新,即可在“功能树配置”看到新增的微服务菜单,按需进行菜单权限配置。
微前端页面改造
在第三方系统开发过程中,可以引入 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),"共享数据");
使用案例
完整案例
<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("当前不是微前端页面")
}