基於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微前端項目的通信方案

基於qiankun微前端的部署方案


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM