一、qiankun 官方提供的通信方式 - Actions 通信。
适用场景:适合业务划分清晰,比较简单的微前端应用,一般来说使用第一种方案就可以满足大部分的应用场景需求。
1、api
qiankun内部使用initGlobalState(state)定义全局状态,该方法执行后返回一个MicroAppStateActions实例,实例中包含三个方法:
- setGlobalState
- offGlobalStateChange
注:
- setGlobalState:设置 globalState - 设置新的值时,内部将执行 浅检查,如果检查到 globalState 发生改变则触发通知,通知到所有的 观察者 函数。
- onGlobalStateChange:注册 观察者 函数 - 响应 globalState 变化,在 globalState 发生改变时触发该 观察者 函数。
- offGlobalStateChange:取消 观察者 函数 - 该实例不再响应 globalState 变化。
2、具体实现
(1)主子应用配置
第一步:在主应用src目录下新建actions.ts文件,initGlobalState设置全局状态actions并导出供其他组件使用。
主应用:src/actions.ts
// 此action文件为定义微应用之间全局状态
// 引入qiankun的应用间通信方法initGlobalState
import { initGlobalState, MicroAppStateActions } from 'qiankun';
const initialState = {
//这里可以写初始化数据
// x:1
project_id: ''
};
const actions: MicroAppStateActions = initGlobalState(initialState); //初始化state
export default actions;
?
第二步:在主应用全局引入人actions实例并在注册子应用时通过props传递全局状态actions。
common/config/registerAppConfig.ts
import actions from '@/actions';
export const registerAppConfig = [
{
name: 'databi',
entry: process.env.VUE_APP_DATABI_ENTRY,
container: '#appContainer',
activeRule: process.env.VUE_APP_ROUTER_BASE + 'app/databi',
props: { data: 'child子应用', mainAppRouter: history, store, router, actions }
},
}
第三步:配置子应用的全局状态actions。子应用中的全局状态必须要跟主应用中的全局状态变量属性名相同,比如主应用中全局状态变量为{project_id: “项目2”},则子应用中也需要保证在setGloabalState时,要改变该值,也要取相同的变量名。
先在子应用中配置一个空的actions实例,以便以后重新赋值从主应用中传递过来的actions。
function emptyAction(...args) {
// 警告:提示当前使用的是空 Action
console.warn("Current execute action is empty!");
}
// 我们首先设置一个用于通信的Actions类
class Actions {
actions = {
onGlobalStateChange: emptyAction,
setGlobalState: emptyAction
}
constructor() {
}
// 默认值为空Action
// 设置actions
setActions(actions) {
this.actions = actions
}
// 映射
onGlobalStateChange(...args) {
return this.actions.onGlobalStateChange(...args)
}
// 映射
setGlobalState(...args) {
return this.actions.setGlobalState(...args)
}
}
const actions = new Actions()
export default actions
第四步:在mounted的生命周期里注入actions实例
子应用dataApi src/main.ts
/**
* 微应用挂载钩子
* @param props
*/
export async function mount(props: any) {
if (props) {
actions.setActions(props)
}
console.log(`[${appName}] app mounted11111${props}`);
console.log('乾坤子应用容器加载完成,开始渲染 child')
console.log(props.router)
Vue.prototype.parentRouter = props.router;
addEventBus(props);
// await initAppBySelf();
await initPermissionData()
render(props);
}
2)主子应用互相传值
dataApi子应用
<template>
<div class="view__container">
测试应用间传值/跳转
<div class="send__msg-box">
<el-input placeholder="请输入内容" v-model="projectId" class="input-with-select">
<el-button slot="append" icon="el-icon-search" @click="changeDataToMaster()"></el-button>
</el-input>
</div>
</div>
</template>
<script>
import actions from '../../actions';
export default {
name: 'OdeonTest',
data() {
return {
projectId: ''
};
},
mounted() {
actions.onGlobalStateChange(state => {
this.projectId = state.project_id;
}, true); //onGlobalStateChange的第二个参数设置为true,则会立即触发一次观察者函数
},
methods: {
changeDataToMaster() {
//测试子应用修改全局状态project_id,父应用接收
actions.setGlobalState({ project_id: this.projectId });
window.history.pushState(null, '', `/odeon/v2/test-master-action`);
},
}
};
</script>
master主应用
<template>
<div class="view__container">
主应用:
<div class="send__msg-box">
<el-input placeholder="请输入内容" v-model="inputVal" class="input-with-select">
<el-button slot="append" icon="el-icon-search" @click="changeDataToMicro"></el-button>
</el-input>
</div>
<br />
project_id的值为:{{ projectId }}
</div>
</template>
<script>
import actions from '@/actions';
export default {
name: 'OdeonTest',
data() {
return {
inputVal: '',
projectId: ''
};
},
mounted() {
actions.onGlobalStateChange(({ project_id }) => {
this.projectId = project_id;
this.$store.commit('user/setUsername', '1111');
}, true);
},
methods: {
//主应用修改全局状态中的project_id值,在子应用中观察
changeDataToMicro() {
console.log(this.inputVal);
actions.setGlobalState({ project_id: this.inputVal });
this.$router.push({
path: '/app/databi/layout/test'//跳转至dataApi子应用的xxx某页面
})
}
},
};
</script>
webapp子应用(配置同dataApi)
(3)效果图
子应用-主应用
主应用-子应用
子应用——>子应用
?
二、props传值
- 静态,无法监测到值变化
- 在主应用 中注册子应用时,将定义好的msg通过props参数传递给子应用。
//registerAppConfig.ts
export const registerAppConfig = [
{
name: 'databi',
entry: process.env.VUE_APP_DATABI_ENTRY,
container: '#appContainer',
activeRule: process.env.VUE_APP_ROUTER_BASE + 'app/databi',
//将定义好的数据传递给子应用
props: { data: 'child子应用', mainAppRouter: history, store, router, actions,msg:'12345' }
}, - 在 子应用的main.ts的mounted函数里将接收到的 props 参数内的函数挂在 vue 原型上方便使用,你也可以在其他导出的生命周期函数内得到 props 并按照你的设想去处理。
export async function mount(props: any) {
if (props) {
actions.setActions(props)
}
console.log('子应用挂载')
console.log(`[${appName}] app mounted11111${props}`);
console.log('乾坤子应用容器加载完成,开始渲染 child')
console.log('msg:',props.msg)
// 主应用传递的 方法 挂载原型上
Vue.prototype.parentMsg = props.msg;
// 主应用传递的 路由实例 挂载原型上
Vue.prototype.parentRouter = props.router;
addEventBus(props);
// await initAppBySelf();
await initPermissionData()
render(props);
}
三 、主子应用共享vuex进行状态管理
具体见:浅析Vue项目如何基于Vuex进行qiankun微前端应用间通信方案实践:子应用无vuex共用主应用store的方案(子应用里如何保证主应用store在子应用的响应式)、子应用有独立store与主应用store分离共存的方案 - 古兰精 - 博客园 (转载)
|