应用间通信

更新时间: 2021-01-29 14:44:29

# 划分子应用

在开始介绍 qiankun 的应用通信之前,我们需要先了解微前端架构如何划分子应用。 在微前端架构中,我们应该按业务划分出对应的子应用,而不是通过功能模块划分子应用。这么做的原因有两个:

  1. 在微前端架构中,子应用并不是一个模块,而是一个独立的应用,我们将子应用按业务划分可以拥有更好的可维护性和解耦性。
  2. 子应用应该具备独立运行的能力,应用间频繁的通信会增加应用的复杂度和耦合度。 综上所述,我们应该从业务的角度出发划分各个子应用,尽可能减少应用间的通信,从而简化整个应用,使得我们的微前端架构可以更加灵活可控。

# Actions 通信

这是qiankun官方提供的应用间通信方式

# 通信原理

qiankun内部提供了initGlobalState方法用于注册MicroAppStateActions实例用于通信,该实例有三个方法,分别是:

  1. onGlobalStateChange: (callback: OnGlobalStateChangeCallback, fireImmediately?: boolean) => void, 在当前应用监听全局状态,有变更触发 callbackfireImmediately = true立即触发callback
  2. setGlobalState: (state: Record<string, any>) => boolean, 按一级属性设置全局状态,微应用中只能修改已存在的一级属性
  3. offGlobalStateChange: () => boolean,移除当前应用的状态监听,微应用 umount时会默认调用 我们来画一张图来帮助大家理解(见下图) 我们从上图可以看出,我们可以先注册 观察者 到观察者池中,然后通过修改 globalState 可以触发所有的 观察者 函数,从而达到组件间通信的效果。

# 实战教学

我们将Actions通信和vuex结合起来使用

# 主应用的工作

首先,我们在主应用中注册一个 MicroAppStateActions 实例并导出,代码实现如下:

  // src/shared/actions.js
  import { initGlobalState } from "qiankun";

  const initialState = {};
  const actions = initGlobalState(initialState);

  export default actions;
1
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);
  }
1
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)
    },
}
1
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;
1
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.jsrender 函数中注入,代码实现如下:

// src/main.js

import actions from "@/shared/actions";

//....
//....
/**
* 渲染函数
* 两种情况:主应用生命周期钩子中运行 / 微应用单独启动时运行
*/
function render(props) {
    if (props) {
        // 注入 actions 实例
        actions.setActions(props);
    }
    //.......
}
//....
//.....
1
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);
}
1
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'
       })
   }
}
1
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 来获取用户信息,最后在页面中显示用户信息。代码过于简单就不展示了