应用间通信
# 划分子应用
在开始介绍 qiankun 的应用通信之前,我们需要先了解微前端架构如何划分子应用。
在微前端架构中,我们应该按业务划分出对应的子应用,而不是通过功能模块划分子应用。这么做的原因有两个:
- 在微前端架构中,子应用并不是一个模块,而是一个独立的应用,我们将子应用按业务划分可以拥有更好的可维护性和解耦性。
- 子应用应该具备独立运行的能力,应用间频繁的通信会增加应用的复杂度和耦合度。 综上所述,我们应该从业务的角度出发划分各个子应用,尽可能减少应用间的通信,从而简化整个应用,使得我们的微前端架构可以更加灵活可控。
# Actions 通信
这是qiankun官方提供的应用间通信方式
# 通信原理
qiankun内部提供了initGlobalState方法用于注册MicroAppStateActions实例用于通信,该实例有三个方法,分别是:
onGlobalStateChange:(callback: OnGlobalStateChangeCallback, fireImmediately?: boolean) => void, 在当前应用监听全局状态,有变更触发callback,fireImmediately = true立即触发callbacksetGlobalState: (state: Record<string, any>) => boolean, 按一级属性设置全局状态,微应用中只能修改已存在的一级属性offGlobalStateChange: () => boolean,移除当前应用的状态监听,微应用umount时会默认调用 我们来画一张图来帮助大家理解(见下图)
我们从上图可以看出,我们可以先注册 观察者 到观察者池中,然后通过修改 globalState 可以触发所有的 观察者 函数,从而达到组件间通信的效果。
# 实战教学
我们将Actions通信和vuex结合起来使用
# 主应用的工作
首先,我们在主应用中注册一个 MicroAppStateActions 实例并导出,代码实现如下:
// src/shared/actions.js
import { initGlobalState } from "qiankun";
const initialState = {};
const actions = initGlobalState(initialState);
export default actions;
2
3
4
5
6
7
本例中我们需要通信的数据是用户信息,所以我们封装一下 发送用户信息 和 监听用户信息的函数
// src/utils/action.js
//导入刚刚注册并导出的actions实例
import actions from "@/shared/actions";
//设置用户信息全局状态
export function sendUserInfo(info){
//属性名为userInfo
actions.setGlobalState({ userInfo:info });
}
//监听全局的用户信息
export function watchUserInfo(func,bool){
//注册 观察者 函数
actions.onGlobalStateChange(func,bool);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
然后主应用登录后,使用vuex存储userInfo,然后在App.vue中监听userInfo
//src/App.vue
import {sendUserInfo,watchUserInfo} from "@/utils/actions";
import { mapGetters } from 'vuex'
export default {
name: 'App',
components:{
layout: () => import("@/layout/index.vue")
},
data(){
return {
}
},
computed: {
...mapGetters([
'userInfo'
])
},
watch:{
userInfo(newVal,oldVal){
//监听到userInfo改变的时候发送userInfo
if(newVal){
sendUserInfo(newVal)
}
}
},
mounted() {
//mounted的时候发送一遍userInfo
sendUserInfo(this.userInfo)
},
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# 子应用的工作
我们已经完成了主应用的登录功能,将 userInfo 信息记录在了 globalState 中。现在,我们进入子应用,使用 userInfo 获取用户信息并展示在页面中。
首先来改造我们的Vue子应用,首先我们设置一个 Actions 实例,代码实现如下:
//src/shared/actions.js
function emptyAction() {
// 警告:提示当前使用的是空 Action
console.warn("Current execute action is empty!");
}
class Actions {
// 默认值为空 Action
actions = {
onGlobalStateChange: emptyAction,
setGlobalState: emptyAction
};
/**
* 设置 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
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
创建 actions 实例后,需要为其注入真实 Actions。在入口文件 main.js 的 render 函数中注入,代码实现如下:
// src/main.js
import actions from "@/shared/actions";
//....
//....
/**
* 渲染函数
* 两种情况:主应用生命周期钩子中运行 / 微应用单独启动时运行
*/
function render(props) {
if (props) {
// 注入 actions 实例
actions.setActions(props);
}
//.......
}
//....
//.....
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
从上面的代码可以看出,挂载子应用时将会调用 render 方法,在render 方法中将主应用的 actions 实例注入即可。
然后子应用中也封装一下 发送用户信息 和 监听用户信息的函数
// src/utils/action.js
//导入actions实例
import actions from "@/shared/actions";
//设置用户信息全局状态
export function sendUserInfo(info){
//属性名为userInfo
actions.setGlobalState({ userInfo:info });
}
//监听全局的用户信息
export function watchUserInfo(func,bool){
//注册 观察者 函数
actions.onGlobalStateChange(func,bool);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
然后在子应用 App.vue 页面中监听 globalState中的 userInfo,并存入子应用vuex。代码实现如下:
// src/App.vue
import {watchUserInfo} from "@/utils/actions";
import { mapMutations } from 'vuex'
export default {
name: 'App',
data(){
return{
}
},
mounted() {
watchUserInfo((state, prevState) => {
// state: 变更后的状态; prevState: 变更前的状态
let {userInfo} = state
// 将userInfo 存入 vuex
this.setUserInfo(userInfo)
})
},
methods:{
...mapMutations({
setUserInfo: 'SET_USERINFO'
})
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
最后我们在子应用的 "获取主应用数据" 页面获取 vuex 中的 userInfo,使用 userInfo 来获取用户信息,最后在页面中显示用户信息。代码过于简单就不展示了