【Vue】使用iframe解决多应用整合问题(微前端)
一、需求背景
有老系统需要重构,新做的系统需要做一个大一统的整合,类似一个分类栏目
在菜单位置罗列出有什么子系统应用,点击对应的应用菜单,展示区跳转到相应的子系统应用中
我用Excel简单描述了下系统的页面效果:
二、技术方案
第一种,使用iframe实现,html提供了iframe标签实现页面内访问其他页面的功能
第二种,使用微前端框架,微前端的实现原理很多,iframe或则组件,又或者远程调用....
详细见:https://www.jianshu.com/p/0aaf0daaaeee
基于现实情况的考量:非专业前端开发,项目成员没有前端大神,微前端一无所知,项目初版开发周期短
我们选择的方案是使用iframe实现
三、落地实现
主应用web + 基础应用web 共用 基础应用server的接口
基础应用相当于认证中心,其他业务server只需要认证中心的鉴权即可
所有web使用同一个令牌做前端权限控制(同理后端)
iframe标签内的应用和主应用共享浏览器数据,需要保持在同一个域之内
1、主应用web改造
主应用本身不需要server, 都是对接基础应用的,所以新增api接口
解决web登录,跳转,退出的接口问题
请求基础应用的令牌携带方式和一些其他参数传递的处理也调整:
vuex的用户actions的方法也要调整
这里定义如何存储用户信息,和退出时需要处理的步骤逻辑
因为我的token存储方式和基础应用的存储方式并不一致,同一个token以两种方式存储
在退出登录的时候,一定要同步把基础应用的token删除,那这里是把那边的逻辑代码搬过来了
(见上面logout方法调用 removeJnpfToken)
iframe跳转实现
首先要有侧边应用菜单的选择:
<el-aside class="sub-sys-menu"> <img :src="appLogo" class="app-logo"> <el-menu mode="vertical" class="sub-sys-menu-list" :default-active="getDefaultActiveMenu" @select="whenMenuItemSelect" > <el-menu-item v-for="(m, idx) in sysMenuList" :key="`menu${idx}`" :index="m.key" > <template slot="title"> <div class="sub-sys-menu-item"> <svg-icon :icon-class="m.icon" class="sub-sys-menu-icon" /> <span class="sub-sys-menu-title">{{ m.name }}</span> </div> </template> </el-menu-item> </el-menu> </el-aside>
默认激活的应用,和应用选中后的事件处理
getDefaultActiveMenu() { if (!this.sysMenuList || this.sysMenuList.length === 0) return '' return this.sysMenuList[0].key }, async whenMenuItemSelect(key, keyPath) { await this.isLoginCheck() const app = this.appList.find(x => x.symbol === key) const menu = this.sysMenuList.find(x => x.key === key) this.currentAppUrl = `${app.url}?Authorization=${encodeURIComponent(this.currentToken)}&time=${new Date().getTime()}` this.currentAppTitle = menu.name },
因为点击时不是路由跳转,所以手动追加是否在线判断:
async isLoginCheck() { try { await this.$store.dispatch('UserGetInfo') } catch (error) { await this.$store.dispatch('UserResetToken') this.$router.push({ path: '/login' }) } }
iframe标签去除所有边框,外边距,百分百填充容器元素
<el-main class="frame-container"> <iframe frameborder="0" border="0" marginwidth="0" marginheight="0" width="100%" height="100%" :src="currentAppUrl" /> </el-main>
数据定义部分:
data() { return { color: 'red', appLogo: require('@/assets/sidelogo-light.png'), sysMenuList: [ { name: '经营场所', icon: 'jxxcs', key: 'APP-1001' }, { name: '非经营场所', icon: 'fjyxcs', key: 'APP-1002' }, { name: '等保测评', icon: 'dbcp', key: 'APP-1003' }, { name: 'APP管理', icon: 'app', key: 'APP-1004' }, { name: '系统管理', icon: 'sz', key: 'APP-1005' } ], appList: [ { symbol: 'APP-1001', url: 'https://www.cnblogs.com/mindzone/p/17964919' }, { symbol: 'APP-1002', url: 'https://www.bilibili.com/' }, { symbol: 'APP-1003', url: 'http://192.168.200.45/cp-mng-web/home' }, { symbol: 'APP-1004', url: 'https://www.cnblogs.com/mindzone' }, { symbol: 'APP-1005', url: 'http://192.168.200.45:3000/home' } // { symbol: 'APP-1005', url: 'http://172.17.29.7:3000/home' } ], currentAppUrl: '', currentAppTitle: '', currentUserName: '', currentToken: '', changeFlag: false } }, computed: { ...mapGetters(['userInfo', 'token']) },
页面挂载时,同步当前用户信息:
mounted() { this.currentUserName = this.userInfo.userName this.currentToken = `${this.token}` },
2、子应用免登录访问问题:
因为进入子应用不应该再次输入密码登录访问,在进入主应用的时候已经通过了认证中心的处理
这里以基础应用为例(本身也是子应用)进行改造调整:
在路由权限逻辑中追加一个免登录的跳转,判断路由路径 + 请求参数是否携带令牌,如果符号则设置令牌,继续跳转