When doing the front end of the middle background, we often encounter such pain points:

(1) There are many systems in the middle and background, which are functionally independent, but the operation personnel still hope to have a unified entrance when using them. Multiple projects have to be maintained in a single repository, and over time, the project code gets bigger and bigger and harder to manage. (2) It involves the upgrade of basic components. Since multiple projects may use the same basic component, all projects have to adapt to the upgrade of components before the new component can be used, which is not flexible. (3) Sometimes we want to consolidate projects from different technology stacks into a single front entry page.

Therefore, we can solve the problem through the idea of a micro front end, which can split each system into independent services, have its own independent warehouse, and finally switch between independent sub-projects through a pedestal project, and can give users a smooth experience similar to a single page application.

The effect is as follows:

Let’s go through the process of building and deploying a very simple micro front-end architecture from zero to one to teach you how to use the micro front-end. This paper will be divided into two processes: 1. Development process. 2. Deployment process. Each process is described in three steps: 1. Pedestal project. 2. React subsystem. 3. Vue subsystem.

The development process

Base project

The main application (dock) is not limited to the technology stack, just provide a container DOM, register the micro-application and start. Qiankun:

$YARN add Qiankun # or NPM I Qiankun -SCopy the code

At the beginning of the file, we need to introduce some library functions of Qiankun, introduce the style file and render function of the main application.

// index.js

import { registerMicroApps, runAfterFirstMounted, setDefaultMountApp, start, initGlobalState } from 'qiankun';
import './index.less';

/** * Main application ** can use any technology stack ** * The following are examples of React and Vue
import render from './render/ReactRender';
// import render from './render/VueRender';
Copy the code

Render function react render function vue render function react render function vue

// render/ReactRender.jsx 

import React from 'react';
import ReactDOM from 'react-dom';

/** * Render child application */
function Render(props) {
  const { loading } = props;

  return (
    <>
      {loading && <h4 className="subapp-loading">Loading... </h4>} <div id="subapp-viewport"/ > < / a >); }export default function render({ loading }) {
  const container = document.getElementById('subapp-container');
  ReactDOM.render(<Render loading={loading} />, container);
}
Copy the code
// render/VueRender.js

import Vue from 'vue/dist/vue.esm';

function vueRender({ loading }) {
  return new Vue({
    template: ` 
      

Loading...

`
, el: '#subapp-container', data() { return{ loading, }; }}); } let app =null; export default function render({ loading }) { if(! app) { app = vueRender({ loading }); }else{ app.loading = loading; }}Copy the code

The render function defines the subsystem mount element: id=’ subapp-viewPort ‘. And render the loading state before the subsystem is actually mounted to the target element. The element id=’subapp-container’ to which the two applications are mounted is defined in the HTML template:

//index.html<! DOCTYPE html> <html lang="en">

<head>
  <meta charset="UTF-8"</title> </head> <body>
  <div class="mainapp"> <! -- Title bar --> <headerclass="mainapp-header"> < H1 > Full stack programming </h1> </header> <divclass="mainapp-main"> <! -- Sidebar --> <ulclass="mainapp-sidemenu">
        <li onclick="push('/reactapp')">reactapp</li>
        <li onclick="push('/vue')">Vue</li> </ul> <! -- subapp --> <main id="subapp-container"></main>
    </div>
  </div>
  
  <script>
    function push(subapp) { history.pushState(null, subapp, subapp) }
  </script>
</body>
</html>
Copy the code

The apps in the two Render functions are mounted into the “child app” element. The style is as follows:

// index.less

// The main application should use the reset style carefully
body {
  margin: 0;
}

.mainapp {
  // Prevent style overwriting applied to quilt
  font-family: -apple-system, BlinkMacSystemFont, Segoe UI, PingFang SC, Hiragino Sans GB, Microsoft YaHei, Helvetica Neue, Helvetica, Arial, sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol;
  line-height: 1;
}

.mainapp-header {
  >h1 {
    color: #333;
    font-size: 36px;
    font-weight: 700;
    margin: 0;
    padding: 36px;
  }
}

.mainapp-main {
  display: flex;

  .mainapp-sidemenu {
    width: 130px;
    list-style: none;
    margin: 0;
    margin-left: 40px;
    padding: 0;
    border-right: 2px solid #aaa;

    >li {
      color: #aaa;
      margin: 20px 0;
      font-size: 18px;
      font-weight: 400;
      cursor: pointer;

      &:hover {
        color: #444;
      }

      &:first-child {
        margin-top: 5px; }}}}// Subapplication area
#subapp-container {
  flex-grow: 1;
  position: relative;
  margin: 0 40px;

  .subapp-loading {
    color: #444;
    font-size: 28px;
    font-weight: 600;
    text-align: center; }}Copy the code

Since we use react mode in the main application, we also need to install related packages:

npm install react react-dom -S
Copy the code

The entire package.json file is as follows for your reference:

//package.json

{
  "name": "main"."version": "1.0.0"."description": ""."main": "index.js"."scripts": {
    "start": "webpack-dev-server"."test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": ""."devDependencies": {
    "@babel/core": "^ 7.7.2"."@babel/plugin-transform-react-jsx": "^ 7.7.0"."@babel/preset-env": "^ 7.7.1." "."babel-loader": "^ 8.0.6"."css-loader": "^ 3.2.0"."html-webpack-plugin": "^ 3.2.0"."less-loader": "^ 6.2.0"."style-loader": "^ 1.0.0"."webpack": "^ 4.41.2"."webpack-cli": "^ 3.3.10"."webpack-dev-server": "^ 3.9.0"."cross-env": "^ 7.0.2"
  },
  "dependencies": {
    "qiankun": "^ against 2.4.1." "."react": "^ 16.13.1"."react-dom": "^ 16.13.1"."vue": "^ 2.6.11." "}}Copy the code

Loading the loading state and defining the loader function is the first step after importing the necessary packages and functions:

//index.js

/** * Step1 initialize the application (optional) */
render({ loading: true });

const loader = loading => render({ loading });
Copy the code

The second step is to register sub-applications through registerMicroApps, in which parameters include subsystem name and entrance. Since we start services from multiple ports in the development process, the development stage is set as port 3000 and port 9000 respectively. Container is the ID of the render subsystem defined by the render function above. Loader is the defined render function that takes a parameter to see if it is loading. ActiveRule is the route to the active subsystem.

//index.js

/** * Step2 register child app */
registerMicroApps(
  [
    {
      name: 'reactapp'.// entry: '/child/reactapp/',
      entry: 'http://localhost:3000',
      container: '#subapp-viewport',
      loader,
      activeRule: '/reactapp',
    },
    {
      name: 'vue'.// entry: '/child/vue/',
      entry: 'http://localhost:9000',
      container: '#subapp-viewport',
      loader,
      activeRule: '/vue',
    }
  ],
  {
    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); },]});const { onGlobalStateChange, setGlobalState } = initGlobalState({
  user: 'qiankun'}); onGlobalStateChange((value, prev) => console.log('[onGlobalStateChange - master]:', value, prev));

setGlobalState({
  ignore: 'master',
  user: {
    name: 'master',}});Copy the code

The third step is to set the subsystem activated by default:

//index.js /** * Step3 Set the child application to enter by default */ setDefaultMountApp(' reactApp ');Copy the code

Set runAfterFirstMounted hook to start() :

//index.js /** * Step4 Start application */ start(); runAfterFirstMounted(() => { console.log('[MainApp] first app mounted'); });Copy the code

In the next article, we’ll continue with how to configure the React subsystem.