基于qiankun微前端初步实现方案(待更新)

1,061 阅读2分钟

前言:基于qiankun实现微前端入门(持续更新)

1:主应用(基座的实现)

基础目录

├── qiankun-main // 基座 
└── qiankun-vue-child // vue子应用

app.uve

<template>
  <div class="app">
    <span><router-link to="/">点击跳转到父页面</router-link></span>
    <span><router-link to="/vue">点击跳转到子页面</router-link></span>
    <router-view />
    <div id="vue"></div> <!-- 重点2:子应用容器 id -->
  </div>
</template>

路由配置文件

//配置history模式剩余和vue写法一样
const router = new VueRouter({
  mode:'history',
  base:'',
  routes
})
//vue.config.js 配置
module.exports ={
  devServer: {
    port: 8085,
    headers: {   // 重点1: 允许跨域访问子应用页面
      'Access-Control-Allow-Origin': '*',
    }
  }
}

main.js

import {registerMicroApps, start} from 'qiankun' 
import microApps from './mirco-app' //子应用注册文件
import  '@/store/globalStore'//主应用状态管理器

registerMicroApps(microApps, { //注册配置文件中的子应用
  beforeLoad: app => {
    console.log('before load app.name====>>>>>', app.name)
  },
  beforeMount: [
    app => {
      console.log('[LifeCycle] before mount %c%s', 'color: green;',     app.name)
    },
  ],
  afterMount: [
    app => {
      console.log('[LifeCycle] after mount %c%s', 'color: green;', app.name)
    }
  ],
  afterUnmount: [
    app => {
      console.log('[LifeCycle] after unmount %c%s', 'color: green;', app.name)
    },
  ],
})

子应用配置文件

import action from '@/store/globalStore'

const microApps = [
  {
    name: 'qiankun-vue-child',
    entry: '//192.168.254.87:8888',
    activeRule: '/vue',
    container: '#vue', // 子应用挂载的div
    props: {
      routerBase: '/vue', // 下发路由给子应用,子应用根据该值去定义qiankun环境下的路由
      getGlobalState: action.getGlobalState //主应用给子应用共享的数据
    }
  }
]

export default microApps

主应用状态管理器封装

import {initGlobalState } from 'qiankun'
import Vue from 'vue'

//父应用的初始state
// Vue.observable是为了让initialState变成可响应
let globalState =Vue.observable({
  user:{
    name:'mool',
    age:10
  }
})

const actions = initGlobalState(globalState)

actions.onGlobalStateChange((state,prev)=>{
  console.log('这里处理状态变更逻辑:', state,prev)
})
// 定义一个获取state的方法下发到子应用
actions.getGlobalState = (key) => {
  // 有key,表示取globalState下的某个子级对象
  // 无key,表示取全部
  console.log('globalState:', globalState)
  return key ? globalState[key] : globalState
}

export default actions;

2:子应用(qiankun-vue-child)

main.js

import globalRegister from './store/globalStore'
import routes from './router' //将注册路由提取到main.js中进行
import '../public-path'
import VueRouter from 'vue-router'

Vue.use(VueRouter)
Vue.config.productionTip = false
let instance = null

function render(props = {}) { //获取主应用通过props传来的路由路径
  const {container, routerBase} = props
  const router = new VueRouter({
    //区分是否在qiankun环境下,qiankun环境下加载主应用传入的路由路径
    base: window.__POWERED_BY_QIANKUN__ ? routerBase : '/vue',
    mode: 'history',
    routes
  })
  instance = new Vue({
    router,
    store,
    render: (h) => h(App)
  }).$mount(container ? container.querySelector('#app') : '#app')
}

if (!window.__POWERED_BY_QIANKUN__) {
  render()
}
//子应用需暴露出以下生命周期
export async function bootstrap() {
  console.log('bootstrap')
}

export async function mount(props) {
  globalRegister(store, props)//注册vuex-module
  render(props)
}

export async function unmount() {
  instance.$destroy()
  instance.$el.innerHTML = ''
  instance = null
}

config配置文件

const { name } = require('./package.json')//名称需与主应用中注册的名称对应
module.exports = {
  configureWebpack: {
    output: {
      library: `${name}-[name]`,
      libraryTarget: 'umd',
      jsonpFunction: `webpackJsonp_${name}`,
    }
  },
  devServer: {
    port: process.env.VUE_APP_PORT, // 在.env中VUE_APP_PORT=7788,与父应用的配置一致
    headers: {
      'Access-Control-Allow-Origin': '*' // 主应用获取子应用时跨域响应头
    }
  }
}

子应用状态管理器(注册到vuex-module`)

function registerGlobalModule(store, props = {}) {

  if (!store || !store.hasModule) {
    return;
  }

  // 获取初始化的state
  const initState = props.getGlobalState && props.getGlobalState() || {
    user: {}
  };
  // 将父应用的数据存储到子应用中,命名空间固定为global
  if (!store.hasModule('global')) {
    const globalModule = {
      namespaced: true,
      state: initState,
      actions: {
        // 子应用改变state并通知父应用
        setGlobalState({ commit }, payload) {
          console.log('payload:',payload )
          commit('setGlobalState', payload);
          commit('emitGlobalState', payload);
        },
        // 初始化,只用于mount时同步父应用的数据
        initGlobalState({ commit }, payload) {
          console.log('payloadpayloadpayload:', payload)
          commit('setGlobalState', payload);
        },
      },
      mutations: {
        setGlobalState(state, payload) {
          // eslint-disable-next-line
          console.log('statestatestate:', state,payload)
          if (payload.key){
            state = Object.assign(state[payload.key], payload.data);
          }else{
            state = Object.assign(state, payload);
          }
        },
        // 通知父应用
        emitGlobalState(state) {
          if (props.setGlobalState) {
            props.setGlobalState(state);
          }
        },
      },
    };
    store.registerModule('global', globalModule);
  } else {
    // 每次mount时,都同步一次父应用数据
    store.dispatch('global/initGlobalState', initState);
  }
}

export default registerGlobalModule;

子父应用的通信

import {mapState, mapActions} from 'vuex'

export default {
  name: 'child',
  components: {},
  computed: {
    ...mapState('global', {
      user: state => state.user, // 获取父应用的user信息
    }),
  },
  methods: {
    ...mapActions('global', ['setGlobalState']),
    update() {
      this.setGlobalState({key:'user', data:{name: '张三',age:1}})
    }
  }
}