一、原理
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,则会立即触发一次观察者函数 }
三、效果图