微前端系列讲解--应用集成方案(qiankun+umi+vue)
1. 微前端架构介绍微前端架构是一种类似于微服务的架构,它将微服务的理念应用于浏览器端,即将 Web 应用由单一的单体应用转变为多个小型前端应用聚合为一的应用。考虑到qiankun作为蚂蚁的微前端架构,具备以下特点:①简单。微应用接入像使用接入一个 iframe 系统一样简单,但实际不是 iframe。②能力完备。几乎包含所有构建微前端系统时所需要的基本能力,如样式隔离、js 沙箱、预加载等。③健
1. 微前端项目架构及选型介绍
1.1. 微前端选型概述
微前端架构是一种类似于微服务的架构,它将微服务的理念应用于浏览器端,即将 Web 应用由单一的单体应用转变为多个小型前端应用聚合为一的应用。考虑到qiankun作为蚂蚁的微前端架构,具备以下特点:①简单。子应用接入像使用接入一个 iframe 系统一样简单,但实际不是 iframe。②能力完备。几乎包含所有构建微前端系统时所需要的基本能力,如样式隔离、js 沙箱、预加载等。③健壮性值得信赖。在蚂蚁内外经受过足够大量的线上系统的考验及打磨。所以,选择qiankun去构建集成应用项目的微前端架构。
1.2. 项目简要
本文配合实战demo讲解微前端项目和集成方案。
【技术选择】
主应用:umi(Ant Design Pro)
子应用:umi(Ant Design Pro)、vue(vue-element-admin)
路由模式:都用hash
【源码地址】
qiankun-demohttps://gitee.com/weihui007/qiankun-demo
2. 微前端基础配置
2.1. 主应用qiankun配置(umi项目)
(1)安装插件
yarn add @umijs/plugin-qiankun -D
(2)注册子应用
插件构建期配置子应用
export default {
qiankun: {
master: {
apps: [
{
name: 'reactApp', // 唯一 id
entry: 'http://localhost:8091', // html entry
},
{
name: 'vueApp',
entry: 'http://localhost:8092',
},
],
},
},
};
(3)装载子应用
使用路由绑定的方式
export default {
routes: [
// 配置子应用reactApp关联的路由
{
name: 'react子应用',
path: '/subreact',
microApp: 'reactApp',
},
// 配置子应用vueApp关联的路由
{
name: 'vue子应用',
path: '/subvue',
microApp: 'vueApp',
},
],
};
2.2. 子应用qiankun配置
2.2.1. vue项目
(1)入口文件main.js
子应用需要在自己的入口文件导出 bootstrap、mount、unmount 三个生命周期钩子,以供主应用在适当的时机调用。
bootstrap:只会在子应用初始化的时候调用一次,下次子应用重新进入时会直接调用 mount 钩子,不会再重复触发 bootstrap。通常我们可以在这里做一些全局变量的初始化,比如不会在 unmount 阶段被销毁的应用级别的缓存等。
mount:应用每次进入都会调用 mount 方法,通常我们在这里触发应用的渲染方法。
unmount:应用每次 切出/卸载 会调用的方法,通常在这里我们会卸载子应用的应用实例
// 声明一个变量,可以用于卸载
let instance = null
// 挂载到自己的html中,基座会拿到这个挂载后的html插入进去
function render(props = {}) {
const { container } = props
instance = new Vue({
router,
store,
render: h => h(App)
}).$mount(container ? container.querySelector('#app') : '#app')
}
// 独立运行
if (!window.__POWERED_BY_QIANKUN__) {
render()
}
// 子组件的协议,必须暴露三个函数
export async function bootstrap(props) {
console.log('bootstrap函数:', props)
}
export async function mount(props) {
console.log('mount函数:', props)
render(props)
}
export async function unmount(props) {
console.log('unmount函数:', props)
instance.$destroy()
instance = null
}
(2)配置文件vue.config.js
除了代码中暴露出相应的生命周期钩子之外,为了让主应用能正确识别子应用暴露出来的一些信息,子应用的打包工具需要增加如下配置
//修改打包配置
configureWebpack: {
output: {
//输出暴露的名称,假设名称为vueApp
library: 'vueApp', //输出暴露的类型
libraryTarget: 'umd'
}
},
//允许跨域
devServer: {
headers: {
'Access-Control-Allow-Origin': '*'
}
},
2.2.2. react项目(umi项目)
(1)安装 qiankun 插件 @umijs/plugin-qiankun
$ npm i @umijs/plugin-qiankun
# 或者
$ yarn add @umijs/plugin-qiankun
(2)插件注册(config.ts)
export default {
qiankun: {
slave: {}
}
}
(3)配置运行时生命周期钩子(可选)
插件会自动为你创建好 qiankun 子应用需要的生命周期钩子,但是如果你想在生命周期期间加一些自定义逻辑,可以在子应用的 src/app.ts 里导出 qiankun 对象,并实现每一个生命周期钩子,其中钩子函数的入参 props 由主应用自动注入。
export const qiankun = {
// 应用加载之前
async bootstrap(props) {
console.log('bootstrap函数:', props)
},
// 应用 render 之前触发
async mount(props) {
console.log('mount函数:', props)
},
// 应用卸载之后触发
async unmount(props) {
console.log('unmount函数:', props)
}
}
2.3. 主子应用实现通信配置
2.3.1. 主应用umi项目
(1)向umi子应用通信
① 新建src->app.ts文件
import { useState } from 'react';
export function useQiankunStateForSlave() {
const [projectId, setProjectId] = useState<string>('');
return { projectId, setProjectId };
}
② 在组件中使用qiankunStateForSlave
import { useModel } from 'umi';
export function ProComp() {
const { setProjectId } = useModel('@@qiankunStateForSlave');
const onChangePro = () => {
setProjectId('123');
};
return <button onClick={onChangePro}>更改项目信息</button>;
}
(2)向vue子应用通信
① 新建src->action.ts文件
import { initGlobalState } from 'qiankun';
const initialState = {
// 初始化数据
projectId: ''
};
// 初始化 state
const actions = initGlobalState(initialState);
export default actions;
② 在组件中使用actions
import actions from '@/actions';
export function ProComp() {
const onChangePro = () => {
actions.setGlobalState({ projectId: '123' });
};
return <button onClick={onChangePro}>更改项目信息</button>;
}
2.3.2. 子应用vue项目
使用qiankun 官方提供的通信方式 - Actions 通信去实现
(1)新建src->actions.js文件
function emptyAction() {
//设置一个actions实例
// 提示当前使用的是空 Action
console.warn('Current execute action is empty!')
}
class Actions {
// 默认值为空 Action
actions = {
onGlobalStateChange: emptyAction,
setGlobalState: emptyAction
}
/**
* 设置 actions
*/
setActions(actions) {
this.actions = actions
}
/**
* 映射,注册观察者函数,响应 globalState 变化
*/
onGlobalStateChange(...args) {
return this.actions.onGlobalStateChange(...args)
}
/**
* 映射,设置 globalState
*/
setGlobalState(...args) {
return this.actions.setGlobalState(...args)
}
}
const actions = new Actions()
export default actions
(2)入口文件main.js
在mounted的生命周期里注入actions实例
import actions from './actions'
export async function mount(props) {
actions.setActions(props) //注入actions实例
render(props)
}
(3)使用范例
<template>
<div>
<div>这是子应用</div>
<p>接收到的消息: {{mes}}</p>
<button @click= "btnClick">点击向父应用发送消息</button>
</div>
</template>
<script>
import actions from '../actions'//导入实例
export default {
data() {
return {
mes: '',
}
},
mounted() {
actions.onGlobalStateChange((state) => { //监听全局状态
this.mes = state
}, true);
},
methods:{
btnClick(){
actions.setGlobalState({ info: '123'})//改变全局状态
}
}
}
</script>
2.3.3. 子应用umi项目
使用useModel钩子函数
import { useModel } from 'umi';
function MyPage() {
const masterProps = useModel('@@qiankunStateFromMaster');
return <div>{JSON.stringify(masterProps)}</div>;
}
3. 微前端项目集成方案
本文利用微前端架构主要实现如下功能:
把不同项目(子应用)集成到一个平台(主应用)。
【顶部导航栏】可包含主应用页面以及各个子应用,子应用可以是任何技术栈(demo中子应用为react和vue)。
【侧边菜单导航栏】可以是主应用页面的子菜单,也可以是子应用的菜单。
【主内容区域】即为页面主要展示区域。若当行了选择的是子应用,即为子应用展示区域。
具体实现效果如下图:
3.1. 主应用改造
主应用为antd pro项目。菜单模式设置为mix(混合菜单),记得切割菜单,即配置splitMenus为true。通过上述配置结合路由配置,即可实现上述平台布局(顶部全局导航+侧边菜单导航混合)。
路由数据结构(routes.ts)参考如下:
export default [
{
name: '欢迎', // 主应用页面1,在顶部全局导航展示
path: '/welcome',
icon: 'smile',
component: './Welcome',
},
{
name: '管理页', // 主应用页面2,在顶部全局导航展示
path: '/admin',
icon: 'crown',
access: 'canAdmin',
component: './Admin',
routes: [ // 二级菜单,在侧边菜单导航展示
{
name: '二级管理页',
path: '/admin/sub-page',
icon: 'smile',
component: './Welcome',
},
],
},
{
name: 'react子应用', // 子应用1,在顶部全局导航展示
path: '/subreact',
microApp: 'reactApp',
routes: [ // 子应用1的菜单,在侧边菜单导航展示
{
name: 'Welcome',
icon: 'smile',
path: '/subreact/Welcome',
},
],
},
{
name: 'vue子应用', // 子应用2,在顶部全局导航展示
path: '/subvue',
microApp: 'vueApp',
routes: [ // 子应用2的菜单,在侧边菜单导航展示
{
name: 'Example',
icon: 'table',
path: '/subvue/example',
routes: [
{
name: 'Table',
icon: 'table',
path: '/subvue/example/table',
},
],
},
],
},
];
3.2. 子应用改造
根据window.__POWERED_BY_QIANKUN__判断子应用是不是从qiankun运行环境进来的,进行两套环境改造。如果独立运行,则走原来的逻辑,否则需要走改造后的逻辑。
3.2.1. 布局改造
如果子应用是从qiankun运行环境进入,即window.__POWERED_BY_QIANKUN__为true,则去除子应用头部和导航菜单布局。
umi子应用:删除掉所有的自带界面,将ProLayout布局组件设置pure为true。
vue子应用(vue-element-admin): 用条件语句v-if、v-else判断,如果是qiankun环境,则只保留主内容区域。
3.2.2. 登录逻辑改造
把登录权限打通,主应用登录后,切换到子应用不需要再进行登录校验,实现免登录功能。
比如主应用是通过cookie验证用户登录的,当子应用是从qiankun环境进入,则通过对应的cookie判断是否有登录,若已登录则跳过登录直接进入对应页面,若没登录或者登录失效则传值给父应用告诉父应用需要重新登录,然后父应用跳转登录页(可在响应拦截中进行父子应用通讯,实现接口回传token失效等原因告诉父应用需要跳转登录页)。
3.2.3. 其它改造
(1)子应用为vue项目需要判断qiankun环境把路由加上前缀
一般需要修改2个地方:①路由表加上前缀;②路由守卫判断如果没有前缀需加上前缀(用于子应用内部需要做路由跳转)
TIPS:前缀指在主应用中跳转子应用设定的path值。
①参考如下:
如果是qiankun环境,则给路由数据的path(路径)和redirect(重定向)值加上前缀。这样就可以和主应用项目对应的菜单导航栏的跳转路径保持一致。
if (window.__POWERED_BY_QIANKUN__) {
routesData.map(item => {
if (item.path.includes('/')) {
item.path = '/subvue' + item.path
}
if (item.redirect) {
item.redirect = '/subvue' + item.redirect
}
return item
})
}
②参考如下:
如果是qiankun环境,则通过路由守卫 beforeEach对路由跳转前进行判断拦截,如果,跳转的地址没有加有前缀,则加上。否则,会出现404找不到页面。
if (window.__POWERED_BY_QIANKUN__) {
router.beforeEach((to, from, next) => {
if (!to.path.includes('subvue')) {
next({
path: '/subvue' + to.path,
query: to.query
})
} else {
next()
}
})
}
(2)其它
除上述提到的,可以根据自身业务需求进行一些改造,常见的有网络请求封装改造、菜单按钮展示改造以及资源加载路径改造等等。
4. 应用集成逻辑图
仅以umi版本为例,展示如下
【推荐文章】
antd pro项目使用qiankun,loading加载页不消失https://blog.csdn.net/w544924116/article/details/120164997antd pro(ProLayout) mix混合菜单不生效https://blog.csdn.net/w544924116/article/details/120211891Error: Module “xxx“ does not exist in container. / antd pro v5启用qiankun报错 / 同时使用mfsu和qiankun报错https://blog.csdn.net/w544924116/article/details/120123331引用window自定义变量以及ts在window上自定义变量数据类型报错的解决方案https://blog.csdn.net/w544924116/article/details/120251686
感谢您读完本文!如果本文对您有帮助,请点个赞呗,您的点赞是对我最大的支持和认可!
我的公众号:大前端教程,欢迎关注,会定期更新前端知识,希望能帮到您。
更多推荐
所有评论(0)