qiankun实现主应用与微应用间通信的常用方式

1,150 阅读5分钟

上篇文章(qiankun+vite+vue3从零搭建一个微前端架构系统)讲了如何从0搭建一个基于qiankun的微前端架构,这篇文章将探索一下主应用与微应用之间如何实现通信,代码是在上篇文章基础上改的。

一、qiankun 提供的 initGlobalState 方法

1. initGlobalState简介

qiankun中,initGlobalState是用于创建一个全局状态对象的方法。这个全局状态对象可以在主应用和子应用之间共享数据,它基于mitt库实现了一个简单的事件总线机制。通过这个全局状态对象,不同的应用(主应用和子应用)可以进行数据的传递和共享,并且能够响应数据的变化。

2. 在主应用中使用initGlobalState实现通信的步骤

2.1 创建和初始化全局状态对象

新建一个actions.ts文件(src/actions.ts),在这里初始化state,监听全局状态改变

import { initGlobalState, type MicroAppStateActions } from "qiankun";

// 初始化 state
const state = {
    testId: null,
};
export const actions: MicroAppStateActions = initGlobalState(state);

// 监听全局状态
actions.onGlobalStateChange((state, prev) => {
    // state: 变更后的状态; prev 变更前的状态
    console.log('onGlobalStateChange', state, prev);
});

2.2 在主应用main.ts加载子应用生命周期的时候可以改变状态

registerMicroApps(
    ...
    {
        afterMount: ((app) => {
            console.log('main app afterMount', app);
            actions.setGlobalState({ testId: 11111 });
            return Promise.resolve();
        })
    }
)

3 在子应用中使用initGlobalState实现通信的步骤

3.1 子应用中新建src/actions.ts

function emptyAction(...args: any[]) {
    // 警告:提示当前使用的是空 Action
    window.console.warn('Current execute action is empty!');
}

// 创建一个Action类
class Actions {
    // 默认值为空 Action
    actions = {
        onGlobalStateChange: emptyAction,
        setGlobalState: emptyAction,
    };

    setActions(actions: {
        onGlobalStateChange: () => void;
        setGlobalState: () => void;
    }) {
        this.actions = actions;
    }

    onGlobalStateChange(...args: any[]) {
        return this.actions.onGlobalStateChange(...args);
    }

    setGlobalState(...args: any[]) {
        return this.actions.setGlobalState(...args);
    }
}

const actions = new Actions();
export default actions;

3.2 子应用加载后,render的时候能拿到主应用传来的action

import actions from './actions';
function render(props?: { container: HTMLElement; onGlobalStateChange: () => void; setGlobalState: () => void; }) {
    if (props) {
        actions.setActions(props); //设置action
    }

    microRouter = router;
    app = createApp(App);
    app.use(microRouter);
    app.use(ElementPlus);
    app.mount(props?.container ? props?.container.querySelector('#container') : '#container');
}

3.3 子应用中修改全局状态

在子应用的App.vue文件中加一个按钮,点击的时候改变全局状态,主应用就能监听到数据改变。

<script setup lang="ts">
import { RouterLink, RouterView } from 'vue-router'
import HelloWorld from './components/HelloWorld.vue'
import actions from './actions';

const click = () => {
    actions.setGlobalState(
        {
            testId: 555555555
        }
    )
}
</script>

<template>
    <header>
        <h2>我的微应用</h2>
        <el-button @click="click">点击向主应用传递数据</el-button>
    </header>

    <RouterView />
</template>

最后我们在页面上点一下,就能在控制台看到主应用监听处的log打印出来了。 在这里插入图片描述

通过initGlobalState方法,主应用和子应用之间可以方便地共享和更新全局状态,实现有效的通信。这种方式使得在微前端架构下,不同的应用之间能够更好地协同工作。

二、props属性

上篇文章,在主应用中注册子应用的时候我只配置几个必须的属性,他其实还能配置一个props属性,可以通过这种方式向子应用传数据,在子应用mount之后就能拿到数据

{
  name: 'silvia-micro', // app name registered
  entry: '//localhost:5174', // 微应用的出口地址
  container: '#container', // 微应用挂载的容器id
  activeRule: '/silvia-micro', // 微应用激活路由规则
  props: {
     mainInfo: {
       name: 'zhangsan'
     }
 }
}

需要注意的是,使用props通信还是有点限制的:

单向数据流限制props通常遵循单向数据流原则,即从主应用流向子应用。这在一定程度上限制了子应用对数据的更新方式。如果子应用需要更新props中的数据,不能直接修改,而是需要通过回调函数或者其他方式通知主应用进行修改。例如,子应用内部发生了一个事件,需要更新props中的某个用户信息,它可能需要触发一个由主应用传入的回调函数来实现更新。这种间接的更新方式增加了代码的复杂性和理解成本。

实时性和一致性挑战:当主应用中的数据发生变化并通过props传递给子应用时,确保子应用能够及时、准确地更新数据可能会比较复杂。尤其是在多个子应用同时依赖相同props数据的情况下,很难保证数据更新的实时性和一致性。例如,主应用更新了一个全局配置props,但由于网络延迟或者子应用内部的其他异步操作,不同子应用可能会在不同时间点接收到更新后的props,导致数据不一致的情况。

三、基于自定义事件通信

原理:利用浏览器的事件机制,主应用和微应用可以通过发布 / 订阅自定义事件来传递消息。例如,主应用可以发布一个事件,微应用订阅这个事件并在事件触发时执行相应的操作,反之亦然。

  • 主应用App.vue文件:
const event = new CustomEvent('microAppMessage', {
    detail: {
        message: '这是主应用发送的消息'
    }
});

const click = () => {
    window.dispatchEvent(event);
}
  • 子应用App.vue文件
window.addEventListener('microAppMessage', (event) => {
    console.log('微应用收到消息:', event);
});
  • 然后运行在页面上点击按钮,向子应用发送消息,控制台就能看到子应用收到了。

在这里插入图片描述

这里呢就先演示这三种方式,当然还有其他方法,比如基于URL通信,本地存储等方式,但是工作中一般最常用的还是官方提供的initGlobalState方式,作为 qiankun 框架自身提供的功能,它与 qiankun 的其他机制(如微应用的加载、生命周期管理等)能够很好地集成。这种原生支持意味着开发者不需要引入额外复杂的第三方状态管理库,减少了因库冲突或不兼容带来的风险。