The introduction

The erp project of the company has been for many years. In order to facilitate maintenance and iteration, and ensure that modules can be independently deployed and run, after some research, we decided to use the idea of micro front end to split the code. Module Federation of Webpack5 is used to split the modules at the beginning, which can ensure that each Module is deployed independently and published independently. The CI/CD of each sub-application has been run and released to the development environment is no problem. However, different technologies of Module Federation have certain limitations. Some extreme cases cannot be handled. At the same time, JS sandboxes and CSS sandboxes also have certain problems, so we decided to use Qiankun to split them.

This tutorial uses Vue as the base for the main application, tapping into sub-applications of different technology stacks, and a module to explain communication.

The preparatory work

Here we use VUE as the base, so first we need to create the VUE professor frame using VUE-CLI as follows:

  1. Install VUE scaffolding

The installation

yarn global add @vue/cli
Copy the code

Check whether the version is correct

vue --version
Copy the code

Create a project

vue create hello-world
Copy the code

Install qiankun

yarn add qiankun
Copy the code
  1. Install react scaffolding

Use create-react-app as the react scaffold generation tool.

The installation

npx create-react-app my-app
cd my-app
yarn eject
yarn start
Copy the code

ERP front-end architecture design

Erp front-end architecture design is as follows:

  1. Main application (base) : a container used to register sub-applications. Vue is used as the main application, including login, logout, password change, Layout, dynamic routing, public State, etc.
  2. Sub-applications (several) : can use any technology stack;
  3. 3.1. Module Federation: Common components, directives, dictionaries, and tool methods are stored in the main application and shared with the MF (recommended); 3.2. NPM package: encapsulate common components, instructions and tool methods into NPM package. All applications need to update the updated version, which is slightly troublesome;

The detailed design is shown in the figure below:

Routing design

Erp project routes are divided into main routes and sub-routes. The main routes are login, home page, and password change, and the sub-routes are the routing modules of each module, as shown below

The main road by

  1. In this case, you need to check whether the user is logged in. If not, the user is redirected to the login page.
  2. Login page After login, obtain the menu permission, filter routes based on all configured modules, and configure dynamic routes.
  3. Then check whether it is the start page. If it is not the home page, go to the subapplication route of the current start page

Zi lu by

  1. The sub-application gets the menu permission returned by the main application and configures dynamic routing
  2. When the active application determines the boot device, it searches for a matching route for the child application of the current boot device

Based on the route design, you can complete the route matching of the primary and secondary applications, as shown in the following figure

Build the main application base

After the scaffolding was built, the main application base was rebuilt according to the tutorial on Qiankun’s website. The first step was to register the micro-application information in the entry file, create the micro-application container, set the default route, and start up

Register the micro-application information in the main application entry file main.js and start it with the following code:

/ / the main js file
/ /...

// Register the micro-application information
registerMicroApps(
  [
    {
      name: 'sub-vue'.entry: '//localhost:7001'.container: '#subapp-viewport'.activeRule: '/sub-vue'.props: {
        shared,
      },
    },
  ],
  {
    beforeLoad: [
      (app) = > {
        console.log('[LifeCycle] before load %c%s'.'color: green; ', app.name)
      },
    ],
    beforeMount: [
      (app) = > {
        console.log('[LifeCycle] before mount %c%s'.'color: green; ', app.name)
      },
    ],
    afterUnmount: [
      (app) = > {
        console.log('[LifeCycle] after unmount %c%s'.'color: green; ', app.name)
      },
    ],
  }
)

// Set the default route
setDefaultMountApp('/sub-vue')
// Start and turn on strict sandbox mode
start({ experimentalStyleIsolation: true })

/ /... App. Vue file
// Set the child application container
<div id="subapp-viewport"></div>
Copy the code

Qiankun API

  1. registerMicroApps(apps, lifeCycles?) : Basic configuration information for registering micro-applications. When the browser URL changes, it will automatically check the activeRule rule registered for each micro-application, and the application that meets the rule will be automatically activated. 1.1. Apps: Mandatory, micro-app registration information 2.2. LifeCycles: Declaration cycle
  2. start(opts?) : Boot QIANkun, OPTS: Optional
  3. SetDefaultMountApp (appLink): Sets the micro app that is entered by default after the main app starts
  4. loadMicroApp(app, configuration?) : Manually loads a microapplication
  5. prefetchApps(apps, importEntryOpts?) : Manually preloads specified microapplication static resources
  6. InitGlobalState (state): defines the global status and returns the communication method. This method is recommended for the active application. Microapplications obtain the communication method from props.
  7. SetGlobalState (state): Sets global status by level 1 attribute. Only existing level 1 attributes can be modified in microapplications
  8. onGlobalStateChange(callback: OnGlobalStateChangeCallback, fireImmediately? : Boolean) => void: listens for state changes
  9. OffGlobalStateChange () => Boolean: Remove status listening for the current application, called by default when umount microapplications

Some of the apis are listed here. Please visit qiankun website for more details

Build vUE microapplication

Create a VUE scaffold using VUe-CLI and install the required dependencies

  1. The configuration in vue.config.js is as follows:
const { name } = require('.. /package.json')

module.exports = {
  // publicPath: '/subapp/sub-vue',
  chainWebpack: (config) = > config.resolve.symlinks(false),
  configureWebpack: {
    output: {
      // Package the child application into the UMD library format
      library: `${name}-[name]`.libraryTarget: 'umd'.jsonpFunction: `webpackJsonp_${name}`,}},devServer: {
    port: 7001.headers: {
      'Access-Control-Allow-Origin': The '*',,}}}Copy the code

When the libraryTarget in webPack is set to UMD or Window, all modules in the Library are exposed globally and the main application can be retrieved through the microapplication lifecycle hook. Headers in devServer must be cross-domain; otherwise, the master application cannot obtain micro-application information.

  1. Create a public-path.js file under SRC, add the following code, and import it in main.js (must be introduced at the beginning)
// public-path.js
if (window.__POWERED_BY_QIANKUN__) {
  // eslint-disable-next-line no-undef
  __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__
}

// main.js
import './public-path'
Copy the code
  1. The main.js configuration declares the cycle as follows
import './public-path'
import Vue from 'vue'
import App from './App.vue'
import routes from './router'
import store from './store'
import VueRouter from 'vue-router'
import actions from './shared/actions'
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'

Vue.config.productionTip = false
let instance = null

Vue.use(ElementUI)

function render(props = {}) {
  if (props) {
    // Inject an Actions instance
    actions.setActions(props)
  }

  const { container } = props
  const router = new VueRouter({
    base: window.__POWERED_BY_QIANKUN__ ? '/sub-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('[vue] vue app bootstraped')}export async function mount(props) {
  console.log('[vue] props from main framework', props)

  props.onGlobalStateChange((state, prev) = > {
    // state: state after the change; Prev Status before change
    console.log('Global state listened by child application', state)
  })

  // Register subapplication routes
  props.setGlobalState({ routes })

  render(props)
}

export async function unmount() {
  instance.$destroy()
  instance.$el.innerHTML = ' '
  instance = null
}
Copy the code
  1. Main application Registers the newly added Vue application in the main.js file
registerMicroApps(
  [
    {
      name: 'sub-vue'.entry: '//localhost:7001'.container: '#subapp-viewport'.activeRule: '/sub-vue'.props: {
        shared,
      },
    },
  ],
Copy the code

At this point, the Vue microapplication is configured, and when you start the master application, you can see the information about the newly added child application in the master application.

Build the React micro application

Follow the tutorial above to create a React scaffolding using create-react-app, expose the Webpack configuration using the YARN eject command, and then do the following configuration

  1. inconfig/webpack.config.jsThe configuration is as follows:
output: {
      // Package the child application into the UMD library format
      library: `${name}-[name]`.libraryTarget: 'umd'.jsonpFunction: `webpackJsonp_${name}`,},Copy the code
  1. inconfig/webpackDevServer.config.jsConfigure headers cross-domain as follows:
    headers: {
      'Access-Control-Allow-Origin': The '*',},Copy the code

Create a public-path.js file under SRC with the following contents:

if (window.__POWERED_BY_QIANKUN__) {
  // eslint-disable-next-line no-undef
  __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__
}
Copy the code
  1. Will be newly createdpublic-path.jsFile, in the entry filesrc/index.jsThe beginning of the file is imported and configuredqiankunLife cycle, as follows:
import './public-path'
import React from 'react'
import ReactDOM from 'react-dom'
import './index.css'
import App from './App'
import 'antd/dist/antd.css'
import actions from './shared/actions'

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

function render(props = {}) {
  if (props) {
    // Inject an Actions instance
    actions.setActions(props)
  }
  ReactDOM.render(
    <React.StrictMode>
      <App />
    </React.StrictMode>.document.getElementById('root'))}export async function bootstrap() {
  console.log('react app bootstraped')}export async function mount(props) {
  console.log('reactApp mount', props)
  render(props)
}

export async function unmount() {
  console.log('react unmount')
  ReactDOM.unmountComponentAtNode(document.getElementById('root'))}Copy the code
  1. Register the React application in the main application, as shown below
// Register the micro-application information
registerMicroApps([
  {
    name: 'sub-vue'.entry: '//localhost:7001'.container: '#subapp-viewport'.activeRule: '/sub-vue'.props: {
      shared,
    },
  },
  {
    name: 'sub-react'.entry: '//localhost:7002'.container: '#subapp-viewport'.activeRule: '/sub-react'.props: {
      shared,
    },
  },
])
Copy the code

The React micro application has been built and is ready to be launched

Master communication management

For the communication of the master application, Qiankun provides apis as follows

  1. InitGlobalState (state): defines the global status and returns the communication method. This method is recommended for the active application. Microapplications obtain the communication method from props.
  2. SetGlobalState (state): Sets global status by level 1 attribute. Only existing level 1 attributes can be modified in microapplications
  3. onGlobalStateChange(callback: OnGlobalStateChangeCallback, fireImmediately? : Boolean) => void: listens for state changes
  4. OffGlobalStateChange () => Boolean: Remove status listening for the current application, called by default when umount microapplications

First of all, for simple master communication, using the QIANkun API is enough for me

  1. Define global state in the main application as follows:
import { initGlobalState } from 'qiankun'

const initialState = { routes: []}const actions = initGlobalState(initialState)

export default actions
Copy the code

Initializes the global state, can be defined using the above actions. OnGlobalStateChange listening state, implementation principle is the observer pattern, specific operation is as follows:

actions.onGlobalStateChange((state, prev) = > {
  console.log('New State:', state)
  console.log('Last Status:', state)
  store.commit('SET_ROUTES', state.routes)
})
Copy the code

Once the status value has changed, it can be stored in the state management of the current technology in use, in this case vuEX

  1. The child application gets the state of the master application

When registering a micro-application, the active application passes the global status to the sub-application through the props actions. The sub-application can process data using the actions obtained by the props, or it can return data of the sub-application to the active application through the actions.setGlobalState, as shown in the following figure

export async function mount(props) {
  console.log('[vue] props from main framework', props)

  props.onGlobalStateChange((state) = > {
    // state: state after the change; Prev Status before change
    console.log('Global state listened by child application', state)
  })

  // Register subapplication routes
  props.setGlobalState({ routes })

  render(props)
}
Copy the code

When the value of the global state is set, all the places listening to the change of the global state will return the latest state, so as to realize the communication between the master and the child.

For complex state management, the logic is shown below

From here, the integration of different technology stacks was realized using Qiankun. The demo git repository

Reference documentation

  1. vue-cli
  2. create-react-app
  3. qiankun