qiankun + vue3 实现子应用 vuex 数据共享

3,089 阅读2分钟

前言

该项目是 vue2 基座 + vue2 子模块(module federation) + vue3 子应用,需要解决 vue2 基座与 vue3 子应用之间的 vuex 数据共享。 思路:借助于 qiankun 内 initGlobalState 去生成一份共享数据,通过传递给子应用 setGlobalStateonGlobalStateChange 方法去让子应用可以修改共享数据和监听到共享数据改变之后,子应用通过 onGlobalStateChange 去修改其 vuex 内的数据。

实现

// 基座
import { initGlobalState } from 'qiankun';
// 初始化共享数据
const { onGlobalStateChange } = initGlobalState({value: '1'})
onGlobalStateChange((state, prev) => {
  // state: 变更后的状态, prev 变更前的状态, 这步可以不加,只是为了调试
  console.log('main-app onGlobalStateChange', state, prev)
})
// vue3 子应用
// store/index.js
import { createStore } from 'vuex'
import cloneDeep from 'lodash/cloneDeep';

const state = {
  store: {}
}

const getters = {
  value: state => state.store.value
}

const mutations = {
  initStore(state, data) {
    state.store = data
  },
  setStore(state, datay) {
    state.store = {
      ...state.store,
      ...data
    }
  }
}

const actions = {
  increment ({ commit }) {
    commit('setStore', {
      value: '2'
    })
  }
}

const initStore = {
  actions,
  getters,
  state,
  mutations,
  modules: {},
  strict: false,
  plugins: [],
}

function initStore(props) {
  const QiankunDataSharePlugin = (store) => {
    let prevState = cloneDeep(store.state)
    // 当 store 初始化后调用,使用 vuex 插件去监听每一次的数据变化,将变化后的数据同步给父容器
    store.subscribe((mutation, state) => {
      // 每次 mutation 之后调用
      const nextState = cloneDeep(state)
      // 这一步不是特别严谨,因为特殊的数据类型,比如 Date,Function,即便是不同,但是也会被 stringify 解析为一样的,可以使用 lodash 的深比较 isEqual,但是比较浪费性能,看项目取舍
      if(JSON.stringify(nextState) !== JSON.stringify(prevState)) {
        prevState = nextState
        // 微应用中store变更后,将状态更新到主应用
        props.setGlobalState &&
        props.setGlobalState({...state.store})
      }
    })
  }

  const storeInstance = createStore({
    ...initStore,
    plugins: [QiankunDataSharePlugin]
  })

  // 主应用状态变化后,同步到微应用
  props.onGlobalStateChange &&
  props.onGlobalStateChange(
    (state, prev) => {
      storeInstance.commit('initStore', state)
      console.log('vue3-app onGlobalStateChange', state, prev)
    }, true
  )

  return storeInstance
}

export default initStore;


// main.js
let instance = null;
// 子应用挂载的时候去实例化 vue app 和挂载全局方法
export async function mount(props) {
  render(props);
  instance.config.globalProperties.$onGlobalStateChange = props.onGlobalStateChange;
  instance.config.globalProperties.$setGlobalState = props.setGlobalState;
}

// 假如是乾坤应用,执行
if (!window.__POWERED_BY_QIANKUN__) {
  render();
}

// 重点是 store(props),传入 props 去获取 onGlobalStateChange 和 setGlobalState 方法
function render(props) {
  const { container } = props;
  instance = createApp(App);
  instance.use(store(props)).mount(container ? container.querySelector('#app') : '#app');
}