微前端qiankun 实践应用

1,884 阅读4分钟

写在前面:微服务风生水起,微前端也随之而来。最近开发一大型erp系统,决定使用微前端框架,看过几个大厂的各种微前端框架,最后决定使用qiankun。主要是相比于其他框架,qiankun 的生态更丰富一些,部分其他大厂的文档写的实在是一眼难尽。也可能是小弟学艺不精,不能感受到文档的玄妙之处

以下文档以vue框架作为基础框架

image.png

微前端qiankun配置

配置内容我将从以下几个点进行分开梳理

  1. 主应用基座配置
  2. 子应用配置
  3. 主子应用通信问题处理
  4. 关于nginx配置发布 内容较多,且容易遗漏

主应用基座配置

主要内容:

  • 初始化应用,安装qiankun依赖包
  • 注册子应用信息
  • 修改路由模式(history/hash)
  1. 我这里使用ant-design-pro作为基础框架
git clone --depth=1 https://github.com/vueComponent/ant-design-vue-pro.git main
  1. 安装qiankun依赖包
yarn add qiankun
  1. 注册子应用(qiankun提供两种api) 方式一:registerMicroApps
//main.js
import { registerMicroApps } from 'qiankun'
registerMicroApps([
  {
    name: 'childone', 
    entry:'http://10.100.72.158:8080/qks/',
    container: '#childone',
    activeRule: '/mainqks/childone'
  },
	......
])

方式二:loadMicroApp

    if (!window.qiankunStarted) {
      window.qiankunStarted = true
      this.microApp = loadMicroApp(
        {
          name: 'setpro',
          entry: 'http://your ip/setpro/page1',
          container: '#setpro'
        },
        {
          fetch(url, ...args) {
            return window.fetch(url, ...args)
          }
        }
      )
    }
  1. 新增一个容器页面,用于挂载子应用,start指启动挂载子应用的api
<!-- 子容器挂载区域 -->
    <div class="child-area">
      <div id="res"></div>
      <div id="ordsys"></div>
      <div id="fin"></div>
      <div id="crm"></div>
      <div id="playmini"></div>
    </div>
    ...
    ...
    ...
    import { start } from 'qiankun'
    ...    
    start()
    
  1. 在路由中添加此页面的路由
import Qks from '../views/qiankuns/index.vue'  
{
    path: '/mainqks',
    name: 'mainqks',
    component: Qks
 },
  1. 修改原有ant的路由模式,由后端维护获取改为从后端拿权限,与前端维护的路由进行匹配后store/index.js

ant-design-pro 提供两种权限方式,详见其文档 image.png

  1. 修改package.json中的name属性,保证项目的名称唯一
"name": "your pro name",
  1. ant-design-vue-pro中使用mock,如果要进行发布预览查看效果,需要修改.env的VUE_APP_PREVIEW,由false改为true
VUE_APP_PREVIEW=true

子应用基础配置

主要内容:

  • 克隆基础项目(这里指ant-design-pro)
  • 导出相应生命周期钩子
  • 配置public-path.js
  • 配置微应用打包(主要涉及vue.config.js)
  • 配置devServer
  • 配置mock预览
  1. 克隆基础项目
git clone --depth=1 https://github.com/vueComponent/ant-design-vue-pro.git child-one
  1. 导出相应生命周期钩子(这里的actions是我封装的通信工具,后面会提到)
//main.js
export async function bootstrap () {
  console.log('[vue] vue app bootstraped')
}
export async function mount (props) {
  console.log('[vue] props from main framework', props)
  // 注入actions实例
  actions.setActions(props)
  render(props)
}
export async function unmount () {
  instance.$destroy()
  instance.$el.innerHTML = ''
  instance = null
  router = null
}
  1. src下新增public-path.js文件,需要在main.js中引入
/* eslint-disable */
if (window.__POWERED_BY_QIANKUN__) {
    __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__
}

main.js中引入

// 独立运行时
if (!window.__POWERED_BY_QIANKUN__) {
  render()
}
  1. 配置微应用作为子包供基座接入
//vue.config.js
const packageName = require('./package.json').name;
module.exports = {
  output: {
    library: `${packageName}-[name]`,
    libraryTarget: 'umd',
    jsonpFunction: `webpackJsonp_${packageName}`,
  },
};
  1. devServer配置header
devServer:{
  // qiankun相关配置
    headers: {
      'Access-Control-Allow-Origin': '*'
    }
}
  1. 特殊的变量名称修改 bootstrap与qiankun的钩子函数名称重名,这边对它进行重新命名

image.png

router有一个销毁重新赋值的过程,对它进行重新命名

image.png

  1. 完整的main.js代码
// with polyfills
import './public-path'
import 'core-js/stable'
import 'regenerator-runtime/runtime'

import Vue from 'vue'
import App from './App.vue'
import childRouter from './router'
import store from './store/'
import i18n from './locales'
import { VueAxios } from './utils/request'
import ProLayout, { PageHeaderWrapper } from '@ant-design-vue/pro-layout'
import themePluginConfig from '../config/themePluginConfig'

// mock
// WARNING: `mockjs` NOT SUPPORT `IE` PLEASE DO NOT USE IN `production` ENV.
import './mock'

import antBootstrap from './core/bootstrap'
import './core/lazy_use' // use lazy load components
import './permission' // permission control
import './utils/filter' // global filter
import './global.less' // global style
import actions from './actions/index'

Vue.config.productionTip = false

// mount axios to `Vue.$http` and `this.$http`
Vue.use(VueAxios)
// use pro-layout components
Vue.component('pro-layout', ProLayout)
Vue.component('page-container', PageHeaderWrapper)
Vue.component('page-header-wrapper', PageHeaderWrapper)

window.umi_plugin_ant_themeVar = themePluginConfig.theme

let router = null
let instance = null
function render (props = {}) {
  const { container } = props
  router = childRouter
  instance = new Vue({
    router,
    store,
    i18n,
    created: antBootstrap,
    render: (h) => h(App)
  }).$mount(container ? container.querySelector('#app') : '#app')
}
// 独立运行时
if (!window.__POWERED_BY_QIANKUN__) {
  render()
}
export async function bootstrap () {
  console.log('[vue] vue app bootstraped')
}
export async function mount (props) {
  console.log('[vue] props from main framework', props)
  // 注入actions实例
  actions.setActions(props)
  render(props)
}
export async function unmount () {
  instance.$destroy()
  instance.$el.innerHTML = ''
  instance = null
  router = null
}

补充vue3.x相关配置

image.png

image.png

  1. vue.config.js中修改打包输出路径和publicPath,这个要与路由的base保持一致 如基座中子应用配置history路由的标识符为childone,则

image.png 9. 修改原有ant的路由模式,由后端维护获取改为从后端拿权限,与主应用中保持一致 10. 修改.env文件VUE_APP_PREVIEW,与主应用保持一致 11. 修改package.json 理由同主应用中配置

可能遇到的路由问题

image.png

  1. 路由跳转失败、404、反复横跳等,路由相关错误 解决方式:

image.png

举例:

  1. 主子应用都为history模式
  2. 主应用中注册方式如下:
  {
    name: 'childsix', 
    entry: 'http://your ip/nginxtest/',
    container: '#childsix',
    activeRule: '/nginxtest/'
  },

当浏览器中路径变为 <http://your ip/nginxtest/> 时,即触发子应用挂载,这里的/nginx/需要与子应用中设置的router的base相同,即如下:

export default new Router({
  base: '/nginxtest/',
  mode: 'history',
  routes: constantRouterMap
})

当然子应用中的publicPath 也要随之修改 这里的nginxtest标识符需要在主应用中设置通配符,如下,否则,主应用将会无法识别路由而跳转404,这是很容易忘记的一点

  {
    path: '/nginxtest/*',
    component:TestPage
  },