一、概念
之前的一篇文章基於qiankun從零搭建了一套微前端項目,主應用是vue,微應用包括vue、react。內部頁面比較單一需要根據實際業務添磚加瓦,每個微應用應該是嚴格按照業務進行拆分的,但是在實際項目開發過程中,主應用、微應用之間能相互通信是基本的需求。
目前有關微前端通信的方案無非兩種:
- 使用qiankun提供的api-initGlobalState進行通信;
- 通過在主、微應用中定義狀態管理類的方式進行通信;
下面我們就第一種方案進行詳細講解,畢竟官方提供了實現方案。達到的效果如下:
- 主應用作為中轉,進行狀態承接與派發;
- 各個微應用不僅能獲取主應用的狀態變更,還能同步自身狀態到主應用;
- 微應用能獨立運行;
二、主應用
我們先來看看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的修改、追蹤。
因此在每一個微應用中,我們需要做的事情有兩個,即:
- 同步主應用狀態變化到微應用;
- 同步微應用狀態變化到主應用;
下面分別講解在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中的值后,每個微應用中的狀態都會實時更新。當然也可以在狀態同步的關口增加自定義邏輯,使控制更加細粒化。
最后小伙伴有什么想法歡迎留言討論,原創不易,看到這里的希望給個贊再走吧!!
系列相關文章: