使用qiankun整合项目记录

qiankun:点击这里

解决问题:将多个系统或者不同技术框架项目可以整合到一起,以前使用iframe方案

这里的使用的是国内目前比较成熟的qiankun

准备工作: 3个应用

基座:主应用(main-app),使用vue-cli3配合vue2搭建
子应用1:vue-app,使用vue-cli3配合vue2搭建
子应用2:react-app,使用cra

子应用路由模式是history

1、将基座搭建好之后,开始引入qiankun,注册微应用,

这里我将微应用加载在某一个页面中,所以不在main.j始终调用start函数

新建一个qiankun.config.js配置注册的微应用

const state = { user: 'tom' } // 用来传递给子应用

export default [{
  name: 'reactApp',
  entry: '//localhost:3000',
  container: '#container',
  activeRule: '/portal/react-app'
},
{
  name: 'vueApp',
  entry: '//localhost:7200',
  container: '#container',
  activeRule: '/portal/vue-app',
  props: state,  // 使用props传递参数
}]

main.js中引入qiankun.config.js并且注册

import { registerMicroApps, initGlobalState } from 'qiankun'
import APPS from '../config/qiankun.config.js'

registerMicroApps(APPS, {
  beforeLoad: (app) => console.log('before load', app.name), // 生命周期
  beforeMount: [
    app => {
      console.log('[LifeCycle] before mount %c%s', 'color: green;', app.name);
    },
  ]
});

在/src/views中新建Index.vue,我们要在这里加载微应用

<template>
  <!-- 微应用容器 -->
  <div id='container'></div>
</template>

<script>
import { start } from 'qiankun'
export default {
// 在mounted周期调用 start 函数
  mounted () {
    if (!window.qiankunStarted) { // 避免重复调用
      window.qiankunStarted = true;
      start(true);
    }
  }
}
</script>

主应用路由表中也要注意修改,因为我们是在某一页面加载微应用,所以路径后面要加*,这样为了匹配注册子应用时候路径的activeRule字段

{
  path: '/portal/*',
  name: 'portal',
  component: SUB_HOME
}

使用qiankun整合项目记录

注意: 这里的路径需要和子应用中的对应,下面会讲到

以上主应用就搭建好了,这里有一个小插曲,其实一开始主应用是使用vue-cli3+vue3+vue-router4搭建的,但是vue-router4好像不支持通配符的使用,所以将主应用改成vue2的,之后会研究一下为什么支持

2、子应用----vue-app

子应用使用vue-cli3创建项目,创建好之后,需要修改main.js和router.js两个文件

在src下面创建一个public-path.js

if (window.__POWERED_BY_QIANKUN__) {
  // eslint-disable-next-line no-undef
  __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
}

修改router.js,只将路由表导出,将路由表的注册移动到main.js中

import Home from '../views/Home.vue'

const routes = [
  {
    path: '/',
    name: 'Home',
    component: Home
  },
  {
    path: '/about',
    name: 'About',
    component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')
  }
]

export default routes

创建vue.config.js,进行一些配置

注意事项
修改端口,和主应用中注册时候的一致,设置header头允许跨域

const { name } = require('./package');

module.exports = {
  publicPath: '//localhost:7200/',
  devServer: {
    port: 7200,
    headers: {
      'Access-Control-Allow-Origin': '*',
    },
  },
  configureWebpack: {
    output: {
      library: `${name}-[name]`,
      libraryTarget: 'umd', // 把微应用打包成 umd 库格式
      jsonpFunction: `webpackJsonp_${name}`,
    }
  }
}

main.js中需要将poublic-path.js在顶部引入

import './public-path'
import Vue from 'vue'
import VueRouter from 'vue-router'
import App from './App.vue'
import routes from './router'
import store from './store'
Vue.config.productionTip = false
Vue.use(VueRouter)

let router = null;
let instance = null;

function render(props = {}) {
  const { container } = props;
  // 这里的base在以微应用加载时候的路径一定和主应用中的activeRules匹配
  router = new VueRouter({
    base: window.__POWERED_BY_QIANKUN__ ? '/portal/vue-app' : '/',
    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('[vue] vue app bootstraped');
}
export async function mount(props) {
  props.onGlobalStateChange((next, prev) => {
    console.log(next, prev);
  })
  render(props);
}

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

目前为止:vue-app子应用已经成功加载

3、开始react-app子应用

react是使用create-react-app脚手架创建的

在src目录中创建public-path.js

if (window.__POWERED_BY_QIANKUN__) {
  // eslint-disable-next-line no-undef
  __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
}

安装react-router-dom

修改index.js

import './public-path';
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import { BrowserRouter } from 'react-router-dom'


function render(props) {
  const { container } = props;
  ReactDOM.render(<BrowserRouter basename={window.__POWERED_BY_QIANKUN__ ? '/portal/react-app' : '/'}><App /></BrowserRouter>, container ? container.querySelector('#root') : document.querySelector('#root'));
}

if (!window.__POWERED_BY_QIANKUN__) {
  render({});
}

export async function bootstrap() {
  // console.log('[react16] react app bootstraped');
}

export async function mount(props) {
  console.log('===========');
  render(props);
}

export async function unmount(props) {
  const { container } = props;
  ReactDOM.unmountComponentAtNode(container ? container.querySelector('#root') : document.querySelector('#root'));
}

最后的成果:
使用qiankun整合项目记录

使用qiankun整合项目记录

使用qiankun整合项目记录

之后要处理的问题:cookie共享,子应用之间跳转,子应用之间数据可否共享

生命周期

部署到服务有什么坑,之后会进一步实践

未解决的问题:
vue-app子应用在本地刷新之后报404

上一篇:微前端-qiankun


下一篇:package.json和package-lock.json的关系