qiankun microapp

93 阅读3分钟

快速上手

基座

搭建项目

  • 1、初始化项目
npm init react-app-qiankun-main
  • 2、qiankun
yarn add qiankun  或者 npm i qiankun -s
  • 3、项目的目录结构(主服务的项目结构
react-app-qiankun-main
├── .env.local             // 本地环境
├── .env.development.local // 测试环境
├── .env.production.local  // 生产环境
├── README.md
├── node_modules
├── package.json
├── .gitignore
├── public
│   ├── favicon.ico
│   ├── index.html
│   └── manifest.json
└── src
    ├── components
    │     └── Loading.jsx
    ├── store
    │     └── store.js    // 主应用的全局状态
    ├── apps.js           // 子应用配置
    ├── App.css
    ├── App.js            // 基座布局,挂载子应用
    ├── App.test.js
    ├── index.css
    ├── index.js          // 主应用中注册微应用
    ├── logo.svg
    ├── reportWebVitals.js
    └── setupTests.js

基座

.env/.env.development.local (dev和test未做区分)

  REACT_APP_SUB_REACT=//localhost:2233/react
  REACT_APP_SUB_VUE=//localhost:3344/vue
  PORT=1122
  • .env.production.local (生产环境)
  REACT_APP_SUB_REACT=//localhost:2233/react
  REACT_APP_SUB_VUE=//localhost:3344/vue

修改 index.html 挂载dom的默认id,防止与子应用id冲突

  // 默认root => main-root
  <div id="main-root"></div>

新增store/store.js见:qiankun.umijs.org/zh/api#init… ,用户主子服务全局数据通信

 import { initGlobalState } from 'qiankun';

 const initialState = {
   user: {
     name: 'qiankun'
   }
 };

    const actions = initGlobalState(initialState);

 actions.onGlobalStateChange((state, prev) => {
   for(const key in state) {
     initialState[key] = state[key];
   }
 })

 // 非官方api,https://github.com/umijs/qiankun/pull/729
 actions.getGlobalState = (key) => {
   return key ? initialState[key] : initialState;
 }

 export default actions;
 

修改src/App.js主要完成基座页面布局及增加挂载子应用的dom(id="subapp-viewport")

 const  App = (props:any)=> {
   return (
     <>
       <div className="mainapp">
         {/* 标题栏 */}
         <header className="mainapp-header">
           <ul className="mainapp-header-sidemenu">
             {/* 侧边栏*/}
           </ul>
         </header>
         <div className="mainapp-main">
           {/* 子应用 */}
           <main id="subapp-viewport"></main>
         </div>
       </div>
     </>
   );
 }

增加app.ts文件,配置相关的子应用

  import store from './store/store'
  const microApps = [
    {
      name: 'react',
      entry: process.env.REACT_APP_SUB_REACT,
      activeRule: '/react',
      container: '#subapp-viewport',
    },
    {
      name: 'vue',
      entry: process.env.REACT_APP_SUB_VUE,
      activeRule: '/vue',
      container: '#subapp-viewport',
    },
  ]

  const apps = microApps.map(item => {
    return {
      ...item,
      props: {
        routerBase: item.activeRule,
        getGlobalState: store.getGlobalState,
      }
    }
  })

  export default apps

主应用中注册微应用,修改[src./index.js], registerMicroApps(apps, lifeCycles?) ,具体详见:qiankun.umijs.org/zh/api#regi…

  import React from 'react';
  import ReactDOM from 'react-dom';
  import './index.css';
  import App from './App';
  import { registerMicroApps, start, setDefaultMountApp } from 'qiankun';
  import apps from './apps'

  function render({ appContent, loading }) {
    const container = document.getElementById('main-root');
    ReactDOM.render(
      <React.StrictMode>
        <App loading={loading} content={appContent} />
      </React.StrictMode>,
      container,
    )
  }

  const loader = loading => render({ loading });

  render({ loading: true });

  const microApps = apps.map((app => ({
    ...app,
    loader,
  })))
  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)
      }
    ]
  })

  setDefaultMountApp('/react')

  // 启动乾坤
  start(opts?)//opts 可选  
  • 本地启动
  npm start

注册React子应用

  • 1、初始化项目
npm init react-app react-app-qiankun-sub 
  • 2.安装react-app-rewiredreact-router-dom
npm i react-app-rewired --save-dev
npm i react-router-dom --save
  • 3.目录结构
react-app-qiankun-sub
├── .env                 // 本地环境
├── config-overrides.js  // 覆盖create-react-app的webpack配置
├── README.md
├── node_modules
├── package.json
├── .gitignore
├── public
│   ├── favicon.ico
│   ├── index.html
│   └── manifest.json
└── src
    ├── components
    │     └── LibVersion.jsx
    ├── pages
    │     └── Home.jsx
    ├── public-path.js // __webpack_public_path__
    ├── App.css
    ├── App.js         // 子应用布局
    ├── App.test.js
    ├── index.css
    ├── index.js       // 子应用入口,挂载dom导出相应的生命周期钩子
    ├── logo.svg
    ├── reportWebVitals.js
    └── setupTests.js
  • 配置本地环境[.env]增加文件,,主要用于配置本地环境,此处的PORT端口好要与基座中的REACT_APP_SUB_REACT端口保持一致
  PORT=2233
  • 修改 index.html 挂载dom的默认id,防止与基座及其他子应用id冲突
  // 默认root => sub-react-root
  <div id="sub-react-root"></div>
  • 新增[src/public-path.js],__webpack_public_path__
  if (window.__POWERED_BY_QIANKUN__) {
    // eslint-disable-next-line no-undef
    __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
  }
  • 修改[src/App.js]主要完成子页面布局

  • 修改[src/index.js]微(子)应用导出相应的生命周期钩子

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import { registerMicroApps, start, setDefaultMountApp } from 'qiankun';
import apps from './apps'

function render({ appContent, loading }) {
  const container = document.getElementById('main-root');
  ReactDOM.render(
    <React.StrictMode>
      <App loading={loading} content={appContent} />
    </React.StrictMode>,
    container,
  )
}

const loader = loading => render({ loading });

render({ loading: true });

const microApps = apps.map((app => ({
  ...app,
  loader,
})))
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)
    }
  ]
})

setDefaultMountApp('/react')

start();

  • 增加[config-overrides.js],覆盖create-react-app的webpack配置
  const { name } = require('./package');
  module.exports = {
    webpack: config => {
      config.output.library = `${name}-[name]`;
      config.output.libraryTarget = 'umd';
      config.output.jsonpFunction = `webpackJsonp_${name}`;
      return config;
    },
    devServer: (configFunction) => {
      return (proxy, allowedHost) => {
        const config = configFunction(proxy, allowedHost);
        config.historyApiFallback = true;
        config.open = false;
        config.hot = false;
        config.watchContentBase = false;
        config.liveReload = false;
        config.headers = {
          'Access-Control-Allow-Origin': '*',
        };
        return config;
      }
    }
  }
  • 修改 package.json
  "scripts": {
    -   "start": "react-scripts start",
    +   "start": "react-app-rewired start",  //重写start
    -   "build": "react-scripts build",
    +   "build": "react-app-rewired build",
    -   "test": "react-scripts test",
    +   "test": "react-app-rewired test",
    "eject": "react-scripts eject"
  },
  • 本地启动
   npm start

添加vue子应用

  • 初始化子应用
npm install -g @vue/cli-service-global
vue create vue-cli-qiankun-sub
  • 2.安装vue-router
npm i vue-router --save
  • 3.目录结构
vue-cli-qiankun-sub
├── .env                 // 本地环境
├── vue.config.js        // vue可选的配置文件
├── babel.config.js
├── README.md
├── node_modules
├── package.json
├── .gitignore
├── public
│   ├── favicon.ico
│   ├── index.html
│   └── manifest.json
└── src
    ├── components
    │    └── HelloWorld.vue
    ├── router
    │     └── index.js
    ├── views
    │     └── Home.vue
    ├── public-path.js  // __webpack_public_path__
    ├── App.vue         // 子应用布局
    └── main.js         // 子应用入口,挂载dom导出相应的生命周期钩子

🌮 vue子应用(开撸代码)

  • 新增1个.env文件,主要配置本地环境

    此处PORT需要和基座REACT_APP_SUB_VUE端口保持一致

      PORT=3344
    
    • 修改 index.html 挂载dom的默认id,防止与基座及其他子应用id冲突
      // 默认root => sub-vue-root
        <div id="sub-vue-root"></div>
    
  • 新增[src/public-path.js],webpack_public_path

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