一、原理
qiankun 内部提供了 initGlobalState 方法用于注册 MicroAppStateActions 实例用于通信,该实例有三个方法,分别是onGlobalStateChange、setGlobalState、offGlobalStateChange。
1、setGlobalState:
设置 globalState - 设置新的值时,内部将执行 浅检查,如果检查到 globalState 发生改变则触发通知,通知到所有的 观察者 函数。
- 语法:
setGlobalState: (state) => boolean, //按一级属性设置全局状态,微应用中只能修改已存在的一级属性(就是用来修改全局状态的
2、onGlobalStateChange:
注册 观察者 函数 - 响应 globalState 变化,在 globalState 发生改变时触发该 观察者 函数。
- 语法:
onGlobalStateChange: (callback) => void //在当前应用监听全局状态,有变更触发 callback,fireImmediately = true 立即触发 callback
3、offGlobalStateChange:
取消 观察者 函数 - 该实例不再响应 globalState 变化。
- 语法:
offGlobalStateChange: () => boolean //移除当前应用的状态监听,微应用 umount 时会默认调用
4、图解
我们从上图可以看出,我们可以先注册 观察者 到观察者池中,然后通过修改 globalState 可以触发所有的观察者函数,从而达到组件间通信的效果。
二、示例
本示例介绍的是子应用中通过路由守卫,把当前的路由面包屑名称传到主应用中,主应用展示面包屑功能。
主应用
1、在父应用中使用initGlobalState设置全局状态actions并导出供其他组件使用。
actions.js
// 此action文件为定义微应用之间全局状态
// 引入qiankun的应用间通信方法initGlobalState
import { initGlobalState } from 'qiankun';
const initalState = {
// 这里可以写初始化数据
}
//初始化state
const actions = initGlobalState(initalState)
export default actions;
2、在main.js中引入actions实例并在注册子应用时通过props传递全局状态actions:
main.js
import actions from './micro/actions'
// 注册的应用列表
registerMicroApps([
{
name: 'sub2',//应用名字
entry: '//localhost:8082', //子应用的启用地址
container: '#app-vue', //子应用所展示的地址 在App.vue中设置id为app-vue
activeRule: '/sub2', //路由地址
// 通过props实现通信传递值
props: {
actions
}
},
{
name: 'sub1',//应用名字
entry: '//localhost:8081', //子应用的启用地址
container: '#app-vue', //子应用所展示的地址 在App.vue中设置id为app-vue
activeRule: '/sub1', //路由地址
},
]);
3、主应用中的组件要修改全局状态actions,就在此组件中引入actions实例
app.vue
<template>
<div id="app">
<div class="top"></div>
<div class="box">
<div class="left">
<el-menu :default-active="$route.path" router class="el-menu-vertical-demo" @open="handleOpen"
@close="handleClose">
<MenuItem :menu-list="menuList">
</MenuItem>
</el-menu>
</div>
<div class="right">
<div class="bread">
<el-breadcrumb separator-class="el-icon-arrow-right">
<el-breadcrumb-item v-for="(item,index) in crumb" :key="index">{{item.name}}</el-breadcrumb-item>
</el-breadcrumb>
</div>
<div class="main">
<router-view></router-view>
<div id="app-vue"></div>
</div>
</div>
</div>
</div>
</template>
<script>
import actions from '@/micro/actions'
export default {
components: {
MenuItem
},
name: "App",
data() {
return {
menuList:[],
crumb:[]
};
},
mounted() {
//修改全局的actions
let crumb=[]
actions.setGlobalState({ crumb })
console.log("主应用app的actions:",actions)
//主应用监听参数变化,接受参数,展示面包屑功能
actions.onGlobalStateChange((state, prevState) => {
console.log("主应用app观察者,改变前的state为:", prevState);
console.log("主应用app观察者,改变后的state为:", state);
if(state?.crumb){
this.crumb=state.crumb
}else{
this.crumb=[]
}
})
}
</script>
子应用
配置子应用的全局状态Actions,子应用中的全局状态必须要跟主应用中的全局状态变量属性名相同,比如主应用中全局状态变量为{token: “main”},则子应用中也需要保证在setGloabalState时也需要设定相同的变量名。
1、在子应用中配置一个空的actions实例为以后重新赋值从主应用中传递过来的actions:
actions.js
function emptyAction() {
// 警告:提示当前使用的是空 Action
console.warn("Current execute action is empty!");
}
class Actions {
// 默认值为空 Action
actions = {
onGlobalStateChange: emptyAction,
setGlobalState: emptyAction
};
constructor() {
}
/**
* 设置 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;
2、然后在mounted的生命周期里注入actions实例:
main.js
import actions from './actions'
function render(props = {}) {
const { container } = props;
if(props){
actions.setActions(props)
}
instance = new Vue({
router,
render: (h) => h(App),
}).$mount(container ? container.querySelector('#app') : '#app');//主应用中设置的id
}
3、子应用向主应用发送数据(子应用中修改数据,可以在主应用中监听到)
router/index.js
import Vue from 'vue'
import VueRouter from 'vue-router'
import HomeView from '../views/HomeView.vue'
import actions from '@/actions'
Vue.use(VueRouter)
const routes = [{
path: "/",
redirect: "home"
},
{
path: '/home',
name: 'home',
component: HomeView,
meta:{
fullPath:[{name:'首页'}]
}
},
{
path: '/about',
name: 'about',
component: () => import( /* webpackChunkName: "about" */ '../views/AboutView.vue'),
meta:{
fullPath:[{name:'首页'},{name:'关于'}]
}
}
]
const router = new VueRouter({
mode: 'history',
base: window.__POWERED_BY_QIANKUN__ ? '/sub2/' : '/',
routes
})
// 通过路由守卫,把当前的路由面包屑名称传到主应用中
router.afterEach(to => {
console.log("to:", to.meta.fullPath)
actions.setGlobalState({ crumb: to.meta.fullPath });
})
export default router
4、子应用在mounted钩子函数中注册qiankun的观察者函数
这里也可以监听主应用的参数
mounted(){
actions.onGlobalStateChange((state) => {
console.log("我是子应用,我检测到数据了:", state);
}, true);
//onGlobalStateChange的第二个参数设置为true,则会立即触发一次观察者函数
}