大家好~
本文是基于 qiankun
的微前端最佳实践系列文章之 应用间通信篇
,本文将分享在 qiankun
中如何进行应用间通信。
在开始介绍 qiankun
的应用通信之前,我们需要先了解微前端架构如何划分子应用。
在微前端架构中,我们应该按业务划分出对应的子应用,而不是通过功能模块划分子应用。这么做的原因有两个:
- 在微前端架构中,子应用并不是一个模块,而是一个独立的应用,我们将子应用按业务划分可以拥有更好的可维护性和解耦性。
- 子应用应该具备独立运行的能力,应用间频繁的通信会增加应用的复杂度和耦合度。
综上所述,我们应该从业务的角度出发划分各个子应用,尽可能减少应用间的通信,从而简化整个应用,使得我们的微前端架构可以更加灵活可控。
我们本次教程将介绍两种通信方式,
- 第一种是
qiankun
官方提供的通信方式 -Actions
通信,适合业务划分清晰,比较简单的微前端应用,一般来说使用第一种方案就可以满足大部分的应用场景需求。 - 第二种是基于
redux
实现的通信方式 -Shared
通信,适合需要跟踪通信状态,子应用具备独立运行能力,较为复杂的微前端应用。
Actions
通信
我们先介绍官方提供的应用间通信方式 - Actions
通信,这种通信方式比较适合业务划分清晰,应用间通信较少的微前端应用场景。
通信原理
qiankun
内部提供了 initGlobalState
方法用于注册 MicroAppStateActions
实例用于通信,该实例有三个方法,分别是:
setGlobalState
:设置globalState
- 设置新的值时,内部将执行浅检查
,如果检查到globalState
发生改变则触发通知,通知到所有的观察者
函数。onGlobalStateChange
:注册观察者
函数 - 响应globalState
变化,在globalState
发生改变时触发该观察者
函数。offGlobalStateChange
:取消观察者
函数 - 该实例不再响应globalState
变化。
我们来画一张图来帮助大家理解(见下图)
我们从上图可以看出,我们可以先注册 观察者
到观察者池中,然后通过修改 globalState
可以触发所有的 观察者
函数,从而达到组件间通信的效果。
实战教程
我们以 实战案例 - feature-communication 分支 (案例是以 Vue
为基座的主应用,接入 React
和 Vue
两个子应用) 为例,来介绍一下如何使用 qiankun
完成应用间的通信功能。
建议
clone
实战案例 - feature-communication 分支 分支代码到本地,运行项目查看实际效果。
主应用的工作
首先,我们在主应用中注册一个 MicroAppStateActions
实例并导出,代码实现如下:
// micro-app-main/src/shared/actions.ts
import { initGlobalState, MicroAppStateActions } from "qiankun";
const initialState = {};
const actions: MicroAppStateActions = initGlobalState(initialState);
export default actions;
在注册 MicroAppStateActions
实例后,我们在需要通信的组件中使用该实例,并注册 观察者
函数,我们这里以登录功能为例,实现如下:
// micro-app-main/src/pages/login/index.vue
import actions from "@/shared/actions";
import { ApiLoginQuickly } from "@/apis";
@Component
export default class Login extends Vue {
$router!: VueRouter;
// `mounted` 是 Vue 的生命周期钩子函数,在组件挂载时执行
mounted() {
// 注册一个观察者函数
actions.onGlobalStateChange((state, prevState) => {
// state: 变更后的状态; prevState: 变更前的状态
console.log("主应用观察者:token 改变前的值为 ", prevState.token);
console.log("主应用观察者:登录状态发生改变,改变后的 token 的值为 ", state.token);
});
}
async login() {
// ApiLoginQuickly 是一个远程登录函数,用于获取 token,详见 Demo
const result = await ApiLoginQuickly();
const { token } = result.data.loginQuickly;
// 登录成功后,设置 token
actions.setGlobalState({ token });
}
}
在上面的代码中,我们在 Vue 组件
的 mounted
生命周期钩子函数中注册了一个 观察者
函数,然后定义了一个 login
方法,最后将 login
方法绑定在下图的按钮中(见下图)。
此时我们点击 2
次按钮,将触发我们在主应用设置的 观察者
函数(如下图)
从上图中我们可以看出:
- 第一次点击:原
token
值为undefined
,新token
值为我们最新设置的值; - 第二次点击时:原
token
的值是我们上一次设置的值,新token
值为我们最新设置的值;
从上面可以看出,我们的 globalState
更新成功啦!
最后,我们在 login
方法最后加上一行代码,让我们在登录后跳转到主页,代码实现如下:
async login() {
//...
this.$router.push("/");
}
子应用的工作
我们已经完成了主应用的登录功能,将 token
信息记录在了 globalState
中。现在,我们进入子应用,使用 token
获取用户信息并展示在页面中。
我们首先来改造我们的 Vue
子应用,首先我们设置一个 Actions
实例,代码实现如下:
// micro-app-vue/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(