基于qiankun微前端的通信方案
一、概念
之前的一篇文章基于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中的值后,每个微应用中的状态都会实时更新。当然也可以在状态同步的关口增加自定义逻辑,使控制更加细粒化。
最后小伙伴有什么想法欢迎留言讨论,原创不易,看到这里的希望给个赞再走吧!!
系列相关文章: