• 基于qiankun微前端的通信方案


    一、概念

    之前的一篇文章基于qiankun从零搭建了一套微前端项目,主应用是vue,微应用包括vue、react。内部页面比较单一需要根据实际业务添砖加瓦,每个微应用应该是严格按照业务进行拆分的,但是在实际项目开发过程中,主应用、微应用之间能相互通信是基本的需求。

    目前有关微前端通信的方案无非两种:

    1. 使用qiankun提供的api-initGlobalState进行通信;
    2. 通过在主、微应用中定义状态管理类的方式进行通信;

    下面我们就第一种方案进行详细讲解,毕竟官方提供了实现方案。达到的效果如下:

    • 主应用作为中转,进行状态承接与派发;
    • 各个微应用不仅能获取主应用的状态变更,还能同步自身状态到主应用;
    • 微应用能独立运行;

    二、主应用

    我们先来看看initGlobalState(state) 这个api的具体信息:

    • 用法:在主应用中使用,定义全局状态,微应用通过props传参获取;
    • 返回:
      • onGlobalStateChange(callback, Immediately)在当前应用监听全局状态变化;
      • setGlobalState(state)按照一级属性进行状态设置,微应用只能修改一级属性;
      • offGlobalStateChange()移除当前的状态监听,微应用在unmount时默认调用;

    下面我们在主应用中进行初始化:

    import { registerMicroApps, start, setDefaultMountApp, initGlobalState } from 'qiankun'
    
    const { onGlobalStateChange } = initGlobalState({iptValue: ''})
    onGlobalStateChange((state, prev) => {
        // state: 变更后的状态; prev 变更前的状态
        console.log('main-app onGlobalStateChange', state, prev)
    })

    这里我们只简单定义了一个一级属性iptValue,用于在微应用中绑定表单。同时监听状态变化,打印相关信息。

    三、微应用

    主应用中状态初始化成功后,整个state状态由qiankun框架内部进行维护,开发者通过官方提供的api进行state的修改、追踪。

    因此在每一个微应用中,我们需要做的事情有两个,即:

    1. 同步主应用状态变化到微应用;
    2. 同步微应用状态变化到主应用;

    下面分别讲解在vue、react中如何进行状态的双向同步。

    3.1 vue微应用

    我们需要在之前vue入口文件main.js中进行扩展,修改render(props)函数。微应用内部状态管理我们使用vuex,通过vuex提供的api进行状态双向同步。

    第一步新建store/index.js文件:

    const state = {
        store: {}
    }
    
    const getters = {
        iptValue: state => state.store.iptValue
    }
    
    const mutations = {
        initStore(state, data) {
            state.store = data
        },
        setStore(state, data) {
            state.store = {
                ...state.store,
                ...data
            }
        }
    }
    
    const actions = {}
    
    export default {
        actions,
        getters,
        state,
        mutations,
        modules: {},
        strict: false,
        plugins: [],
    }

    这里我们只是定义了store的结构,但是并没有生成vuex的实例对象。

    第二步初始化微应用store,绑定状态同步逻辑:

    import Store from './store/index'
    
    /**
     * 初始化主微应用通信逻辑
     * 1.主应用状态变更同步到微应用;
     * 2.微应用状态变更同步到主应用;
     */
    function initStore(props) {
        const myPlugin = store => {
            let prevState = _.cloneDeep(store.state)
            // 当 store 初始化后调用
            store.subscribe((mutation, state) => {
                // 每次 mutation 之后调用
                let nextState = _.cloneDeep(state)
                if(JSON.stringify(nextState) !== JSON.stringify(prevState)) {
                    prevState = nextState
                    // 微应用中store变更后,将状态更新到主应用
                    props.setGlobalState &&
                    props.setGlobalState({...state.store})
                }
            })
        }
    
        const storeInstance =  new Vuex.Store({
            ...Store,
            plugins: [myPlugin]
        })
    
        // 主应用状态变化后,同步到微应用
        props.onGlobalStateChange &&
        props.onGlobalStateChange(
              (state, prev) => {
                storeInstance.commit('initStore', state)
                console.log('vue-app onGlobalStateChange', state, prev)
            }, true
        )
    
        return storeInstance
    }

    主应用状态变更同步到微应用比较简单,在onGlobalStateChange方法中调用commit进行同步即可。

    微应用状态变更同步到主应用我们使用setGlobalState()方法,关键是要实时监听微应用状态变更。这里我们使用vuex插件提供的派发功能subscribe,同步之前会比较两次状态是否存在变更,否则会陷入死循环。

    第三步为vue实例绑定store:

    function render(props = {}) {
        ...
        
        store = initStore(props)
    
          instance = new Vue({
            router,
    +        store,
            render: h => h(App),
          }).$mount(container ? container.querySelector('#app') : '#app')
    }

    这样vue微应用状态同步逻辑就完成了,就像平时使用vuex一样进行管理即可。当然主、微应用通信的这一份状态最好是单独进行维护的,不应该和微应用内部业务逻辑状态相关联,我们可以使用vuex的modules功能进行实现,这里就不再赘诉有需要再更新。

    3.2 react微应用

    react的实现逻辑和vue都一样,只是不同技术栈代码写法不一样而已,react中我们使用mobx、mobx-react进行状态管理。mobx开箱即用支持装饰器语法,天然存在状态隔离,和react-hook完美搭配。

    第一部新建store文件:

    import { observable, computed, action, reaction } from 'mobx'
    import _ from 'lodash'
    import { MainStoreModel } from '../model/mainApp'
    
    class MainApp {
        static setGlobalState: Function
    
        @observable preStore: MainStoreModel = {}
        @observable store: MainStoreModel = {}
    
        constructor() {}
    
        @computed get iptValue() {
            return this.store.iptValue
        }
    
        @action
        initStore(data: MainStoreModel) {
            this.store = data
        }
    
        @action
        setStore(data: any) {
            const prev = _.cloneDeep(this.store)
            this.store = {
                ...prev,
                ...data
            }
        }
    }
    
    const mainApp = new MainApp()
     
    export { 
        mainApp, 
        MainApp 
    }

    第二步修改入口文件,初始化state同步逻辑

    import { mainApp, MainApp } from './store/mainApp'
    
    /**
     * 初始化主微应用通信逻辑
     * 1.主应用状态变更同步到微应用;
     * 2.微应用状态变更同步到主应用;
     */
    function initStore(props: any) {
        // 将setGlobalState方法绑定到类MainApp静态属性上
        MainApp.setGlobalState = props.setGlobalState ? props.setGlobalState : null
    // 主应用状态变更同步到微应用 props.onGlobalStateChange && props.onGlobalStateChange( (state: MainStoreModel, prev: MainStoreModel) => { mainApp.initStore(state) console.log('react-app onGlobalStateChange', state, prev) }, true ) } export async function mount(props: any) { render(props) + initStore(props) }

    主应用状态同步到微应用逻辑和vue大同小异。不同的是,我们将setGlobalState方法绑定在MainApp类的静态属性上,微应用状态同步到主应用时会用到。

    第三步reaction同步微应用状态到主应用

    我们使用mobx中reaction的特性用于监听store的变化,来实现我们需要的逻辑。

    class MainApp {
      ...
    appReaction
    = reaction( () => this.store, store => { const nextState = _.cloneDeep(store) const prevState = _.cloneDeep(this.preStore) if(JSON.stringify(nextState) !== JSON.stringify(prevState)) { MainApp.setGlobalState&&MainApp.setGlobalState(nextState) this.preStore = nextState } } ) }

    同步逻辑同样需要比较变更前后两份深拷贝状态的差异,防止陷入死循环。 

    完整微前端项目代码在github上。

    四、后记

    到此,修改微应用中state中的值后,每个微应用中的状态都会实时更新。当然也可以在状态同步的关口增加自定义逻辑,使控制更加细粒化。

    最后小伙伴有什么想法欢迎留言讨论,原创不易,看到这里的希望给个赞再走吧!!

    系列相关文章:

    基于qiankun从零搭建微前端项目

    基于qiankun微前端项目的通信方案

  • 相关阅读:
    图像的剪切
    DOS指令大全(二)
    扫描进程
    数据库名、数据库实例、全局数据库名、服务名、SID等的区别
    ORA29807: specified operator does not exist
    TCP/IP网络编程的几个网站
    漂在等待离职的日子(三)
    入职第一天
    入职一周
    漂在等待离职的日子(八)
  • 原文地址:https://www.cnblogs.com/lodadssd/p/14480412.html
Copyright © 2020-2023  润新知