The micro front end is a solution that every senior front end engineer should understand. The author combines theory, application and practice, and combines development and production in order to reduce the difficulties encountered by small partners in the micro-front end of pit mining. It is highly recommended that students who have not practiced this article write the Demo to deepen their understanding and memory.

background

In recent years, the rapid development of front-end technology, to achieve unprecedented prosperity, front-end ecology is full of vitality. At the same time, the front-end architecture has changed dramatically.

From no architecture to multiple pages, from multiple pages to single pages, from single pages to componentization, from componentization to front and back end separation, to today’s micro front end.

Each change was made to better solve the problem. Today, the emergence of micro front-end is no exception. Today’s large front-end projects mainly have the following problems:

  • Centralization: The project is too big, all the functions are centralized, and the project gets bigger and bigger.
  • Low cohesion, high coupling: Due to the excessive variety and complexity of functions, serious interdependence and serious coupling.

1 Introduction to the micro front end

Those of you who want to skip the theory can go straight toDEMOPart of the start

1.1 What is a microfront end?

The micro front end is a kind of technical means and method strategy for multiple teams to jointly build modern Web applications by releasing functions independently.

It usually consists of a master application and one or more sub-applications.

1.2 Core values of the micro front End?

1.2.1 Stack independent

All applications can choose from different technology stacks without having to worry about which one to choose

1.2.2 Can be independently developed and deployed

Microapplications are independent of each other and can be developed and deployed independently. After deployment, the master application automatically completes synchronous update

1.2.3 Incremental updates are available

In the face of a variety of complex scenarios, it is often difficult for us to upgrade or reconstruct the technology stack of an existing system completely, and the micro front end is a very good means and strategy to implement the gradual reconstruction (deployment update)

1.2.4 Independent runtime

State is isolated between each microapplication and run time state is not shared

1.3 Architecture model of micro front-end

1.3.1 Base mode

Manage other applications through one master application. The design difficulty is small, convenient practice, but, the universal degree is low.

1.3.2 Self-organization mode

Applications are equal and there is no model of mutual management. The design is difficult, not convenient to implement, but high versatility.

1.4 what isqiankun ?

Qiankun is a single-SPa-based microfront-end implementation library that aims to make it easier and painless to build a production-ready microfront-end architecture system.

Qiankun was incubated from ant Fintech’s unified access platform for cloud products based on micro-front-end architecture. After being fully examined and polished by a batch of online applications, we extracted its micro-front-end kernel and opened source, hoping to help communities with similar needs to build their own micro-front-end systems more conveniently. At the same time, I also hope that with the help of the community, Qiankun will become more mature and perfect.

At present, Qiankun has served more than 200+ online applications in ant interior, which is absolutely trustworthy in terms of ease of use and completeness.

The above introduction is from qiankun official website

1.5qiankunFeature:

  1. Based on the single-SPA package, provides a more out-of-the-box API.
  2. Technology stack independent, any technology stack applications can use/access, be it React/Vue/Angular/JQuery or other frameworks.
  3. HTML Entry access allows you to access micro applications as easily as using iframe.
  4. Style isolation ensures that styles do not interfere with each other between microapplications.
  5. JS sandbox to ensure no global variable/event conflicts between microapplications.
  6. Resource preloading: Preloads unopened micro-application resources in idle time of the browser to speed up the opening of micro-applications.

1.6 Why Notsingle-spa?

Since Qiankun is based on single-SPA, why don’t we choose single-SPA closer to the bottom layer for development?

The answer is: green, from blue and green in blue.

Based on the secondary development of single-SPA, Qiankun realized more functions (such as HTML Entry) and solved many problems (such as JS isolation and style isolation). Better development and user experience (preloading, etc.).

1.7 Why Notiframe?

Iframe is a native application isolation solution provided by browsers. Due to its strong isolation, it has the following problems:

  1. Development experience: Difficult to solve during developmentiframeCommunication between and style problems, development difficulty.
  2. User experience: Slow andurlOut of sync. Browser refreshiframe urlStatus lost, back forward button unavailable.

See Why Not Iframe for details

2 Application Scenarios

  • History project maintenance and iteration (e.gAngular. js is angular 1.xx, developed projects)
  • Megalithic application (project too large)
  • Cross-technology development (want to develop with different technology stacks)
  • Technical research and reserve (hope to practice cutting-edge technology)

3 qiankun vs single-spa vs iframe

Solution for the independence of | development | deploy style isolated | JS | | | | Entry form usability technology stack | — – | — – | — – | — – | — – | — – | — – | — – | — – | qiankun | ✅ | ✅ | ✅ | ✅ |. Any good HTML | | | single – spa | ✅ | ✅ | ❌ | ❌. | js any general | | | iframe | ✅ | ✅ | ✅ | ✅ |. Any HTML good | | |

3.1 JS Entry vs HTML Entry

Entry indicates the entry form of resources provided by a sub-application

JS Entry is usually done by subapplications typing resources into an Entry script, as in the example of single-SPA. However, this solution has many limitations, such as requiring all resources of the child application to be packaged into a JS bundle, including CSS, images and other resources. In addition to the potential size of the printed package, features such as parallel loading of resources are not available.

HTML Entry is more flexible, directly typing out HTML as the Entry of the sub-application. The main frame can fetch THE static resources of the sub-application by THE way of HTML, and at the same time, the HTML document is stuffed into the container of the main frame as a child node. In this way, not only can the access cost of the main application be greatly reduced, the development mode and packaging mode of the sub-application basically do not need to be adjusted, but also can naturally solve the problem of style isolation between the sub-applications.

4 practiceqiankun

Look at the renderings first

Main application development

The main application can be developed using HTML5 Vue React Angular. I used Vue to develop the main application.

1 installationqiankun

yarn add qiankun
Copy the code

2 Register sub-applications

Note: Entry is the entry to load the child application and must be consistent with the child project; Subprojects must support cross-domain requests.

main.js

import Vue from 'vue'
import App from './App.vue'
import router from './router'

// Introduce the qiankun package
import { registerMicroApps, start } from 'qiankun'
// List of subapplications
const apps = [
  {
    name: 'reactApp'.// The name of the child application is arbitrary
    entry: '//localhost:7100'.// The index.html for this entry will be loaded by default, (the child application must support cross-domain) fetch
    container: '#reactApp'.// The container name (the DOM element ID used to mount the child application) must be the same as the identity of the custom DOM element in the main application (id is recommended)
    activeRule: '/react'.// The rule activated by the child application (will be matched according to the route)
  },
  {
    name: 'vueApp'.// The name of the child application is arbitrary
    entry: '//localhost:7101'.// The default is to load the index.html parse js fetch dynamically (the child application must support cross-domain)
    container: '#vueApp'.// The container name (the DOM element ID used to mount the child application) must be the same as the identity of the custom DOM element in the main application (id is recommended)
    activeRule: '/vue'.// The rule activated by the child application (will be matched according to the route)
    props: { a: 1 } // The data passed to the child application
  },
  {
    name: 'onlineApp'.// app name registered
    entry: '//cicd.alexshan.com/index.html'.// The default loading of the HTML parse JS fetch is dynamic execution (subapplications must support cross-domain)
    container: '#onlineApp'./ / container
    activeRule: '/online' // Active path}]// Register the application
registerMicroApps(apps) 

/ / open
start()

new Vue({
  router,
  render: h= > h(App),
}).$mount('#app')
Copy the code

3 Create a container to mount sub-applications

You can add containers for mounting sub-applications on different pages based on application scenarios

App.vue

<template>
  <div id="app">
    
    <el-menu :router="true" mode="horizontal">
      <! -- Base can put its own route -->
      <el-menu-item index="/">Home</el-menu-item>
      
      <! -- Reference other child apps -->
      <el-menu-item index="/vue">Vue applications</el-menu-item>
      <el-menu-item index="/react">The react application</el-menu-item>
      <el-menu-item index="/online">The react the online application</el-menu-item>
    </el-menu>
    
    <router-view></router-view>
    
    <! The following two containers are used to mount child applications and cannot be deleted.
    <div id="vueApp"></div>
    <div id="reactApp"></div>
    <div id="onlineApp"></div>
  </div>
</template>

<script>
export default {
  name: "App".components: {}};</script>
Copy the code

Main application complete code: github.com/AlexShan200…

React for child application development

There is no need to install the Qiankun dependencies, just export the lifecycle functions by convention

1 Sub-application entry file export life cycle function

index.js

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';

function render(){
  ReactDOM.render(
    <React.StrictMode>
      <App />
    </React.StrictMode>.document.getElementById('root')); }// __POWERED_BY_QIANKUN__ is the global variable of the Qiankun application
if(!window.__POWERED_BY_QIANKUN__){
  render();
}

export async function bootstrap(){}export async function mount() {
  render()
}
export async function unmount(){
  ReactDOM.unmountComponentAtNode( document.getElementById('root'));
}
Copy the code

2 Set Cross-domain licensing, changedwebpackThe configuration file

If the project is built using the create-react-app tool, you will need to install the dependency rewrite configuration

2.1 Installation depends on react-app-rewired

yarn add react-app-rewired -D
Copy the code

2.2 Reconfiguring the project startup script

1 package.json

  "scripts": {
    "start": "react-app-rewired start"."build": "react-app-rewired build"."test": "react-app-rewired test"."eject": "react-app-rewired eject"
  }
Copy the code

2.3 Create the config-overrides. Js configuration file in the root directory of the project

2 config-overrides.js

module.exports = {
    webpack:(config) = >{
        config.output.library = 'reactApp';
        config.output.libraryTarget = 'umd';
        config.output.publicPath = 'http://localhost:7100/';
        return config;
    },
    devServer:(configFunction) = >{
        return function (proxy,allowedHost){
            const config = configFunction(proxy,allowedHost);
            config.headers = {
                "Access-Control-Allow-Origin":The '*'
            }
            return config
        }
    }
}
Copy the code

The routing configuration

The base path is determined by the global variable window.POWERED_BY_QIANKUN

App.js

import React, { lazy, Suspense } from 'react';
import { BrowserRouter as Router, Link, Route, Switch } from 'react-router-dom';
import 'antd/dist/antd.min.css';
import './App.css';
import Home from './pages/Home';

const About = lazy(() = > import('./pages/About'));

const RouteExample = () = > {
  return (
    // Basename must be consistent with the base baseTODO:You can encapsulate a component to solve this problem uniformly
    <Router basename={window.__POWERED_BY_QIANKUN__ ? '/react' :'/'} >
      <nav>
        <Link to="/">Home</Link>
        <Divider type="vertical" />
        <Link to="/about">About</Link>
      </nav>
      <Suspense fallback={null}>
        <Switch>
          <Route path="/" exact component={Home} />
          <Route path="/about" component={About} />
        </Switch>
      </Suspense>
    </Router>
  );
};
export default function App() {
  return (
    <div className="app-main">
    </div>
  );
}
Copy the code

React sub app full code: github.com/AlexShan200…

Vue for sub-application development

1 Sub-application entry file export life cycle function

main.js

/** * The bootstrap function will only be executed once during initialization * the second entry into the child application will directly mount the child application and will not trigger the startup function bootstrap * usually does some global variable initialization * the application level cache will not be destroyed during uninstallation */
export async function bootstrap() {
  console.log('react app bootstraped');
}
/** * The mount method is called every time the application enters, * usually we trigger the application's rendering method here. */
export async function mount(props) {
  console.log(props);
  ReactDOM.render(<App />.document.getElementById('react15Root'));
}
/** * Methods that are called each time the application is switched/unloaded, * usually in this case we uninstall the application instance of the subapplication. */
export async function unmount() {
  ReactDOM.unmountComponentAtNode(document.getElementById('react15Root'));
}
Copy the code

2 Perform different rendering depending on the access source of the subproject

Resolve issues where subprojects cannot be independently accessed during development and deployment

main.js

let router = null;
let instance = null;
// Render function
function render(props = {}) {
  const { container } = props;
  router = new VueRouter({
    base: window.__POWERED_BY_QIANKUN__ ? '/vue' : '/'.// The path '/vue' is the same as the main application activeRule
    mode: 'history'}); instance =new Vue({
    router,
    render: h= > h(App),
  }).$mount(container ? container.querySelector('#app') : '#app');
}

// The global variable __POWERED_BY_QIANKUN__ was rendered
if (!window.__POWERED_BY_QIANKUN__) {
  render();
}
Copy the code

3 the configurationwebpack public path

Resolve incorrect base paths

main.js

// Dynamically add publicPath
if (window.__POWERED_BY_QIANKUN__) {
  // eslint-disable-next-line no-undef
  __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__; // set the usage
}
Copy the code

Complete the main js

import Vue from 'vue'
import VueRouter from 'vue-router';
import App from './App.vue'
Vue.config.productionTip = false
let router = null;
let instance = null;
function render(props = {}) {
  const { container } = props;
  router = new VueRouter({
    base: window.__POWERED_BY_QIANKUN__ ? '/vue' : '/'.// Path needs to be consistent with base applications
    mode: 'history'
  });
  instance = new Vue({
    router,
    render: h= > h(App),
  }).$mount(container ? container.querySelector('#app') : '#app');
}
// Execute different rendering methods depending on the source of access
if (!window.__POWERED_BY_QIANKUN__) {
  render();
}
// Fix the base path error
if (window.__POWERED_BY_QIANKUN__) { // Dynamically add publicPath
  // eslint-disable-next-line no-undef
  __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
}
/ / start
export async function bootstrap() {
  console.log('[vue] vue app bootstraped');
}
/ / a mount
export async function mount(props) {
  console.log('[vue] props from main framework', props);
  render(props);
}
/ / unloading
export async function unmount() {
  instance.$destroy();
  instance.$el.innerHTML = ' ';
  instance = null;
  router = null;
}
Copy the code

Change the child application configuration: develop, build, and package

vue.config.js

const path = require('path');
const { name } = require('./package');
function resolve(dir) {
  return path.join(__dirname, dir);
}
const port = 7101; // Dev port must be consistent with the entry entry of the base (main application)
module.exports = {
  outputDir: 'dist'.assetsDir: 'static'.filenameHashing: true.devServer: {
    hot: true.disableHostCheck: true,
    port,
    overlay: {
      warnings: false.errors: true,},headers: {
      'Access-Control-Allow-Origin': The '*'.// Allow cross-domain resource acquisition at development time}},// Customize the WebPack configuration
  configureWebpack: {
    resolve: {
      alias: {
        The '@': resolve('src'),}},output: {
      // Package the child application into the UMD library format
      library: `${name}-[name]`.libraryTarget: 'umd'.jsonpFunction: `webpackJsonp_${name}`,}}};Copy the code

Vue sub application complete code: github.com/AlexShan200…

Sub-application React (Production Environment)

Build the React sub-application as a normal package, which is the same as normal development.

5 Project Deployment

All projects can be deployed independently, and since each project is packaged as a static page, we use Nginx to deploy the project.

Nginx configuration

  • Cross domain license
  • index.htmlPage redirection

nginx.conf

  1. Address cross-domain permissions by adding cross-domain permissions in the light request header
http {
    server {
        location / {
            add_header 'Access-Control-Allow-Origin' The '*'; # Suggest licensing by domain name
            add_header 'Access-Control-Allow-Credentials' 'true';
            add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
            add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type'; }}}Copy the code
  1. To solvehistoryMode page refresh404
http {
    server {
        location / {
            root   nginx/html; The root directory of the packaged project
            index  index.html index.htm;
            try_files $uri $uri/ /index.html; }}}Copy the code

For the complete nginx configuration file, go to the Github repo

conclusion

Attention to detail

Qiankun’s practice is not complicated, mainly involving registration of master applications, life cycle function export of sub-applications and cross-domain licensing. However, there will be more details in the development and rollout, which need to be noted:

  1. webpackPackage configuration, the package file should beumd
  2. Child applications must be set up for cross-domain licensing
  3. The main application remembers to add mount containers, containersidorclassMust be connected tocontainerThe same

FAQ

  1. qiankunIs the production environment available?

The answer is yes, production is available. Qiankun is ant Financial’s internal practice achievement sharing.

  1. How do you split the front-end application?
    • According to the business
    • According to the authority
    • By upgrade frequency
  2. What are the problems with the micro front end?
    • Code architecture: The adoption of microservices across different organizations is mutually beneficial. However, in a small internal organization, microservices may not be appropriate. Multiple code repositories create a lot of repetitive work, such as administration, development builds, and so on.
    • Deployment process: Microservices are developed independently and deployed independently. If a microservice cannot be deployed independently, then deployment is a pain. At the same time, if an upgradereleaseLaunching more than a dozen services at once is also a pain.

Thank you

Thank you for spending a lot of time to read this article, due to the author’s level and ability is limited, if there is anything wrong in the article, please criticize and correct.

Welcome to leave a message, discuss and learn together. Your comments and likes are a great encouragement to me, and I will be greatly appreciated!

reference

  • micro-frontends
  • single-spa
  • qiankun
  • Probably the most complete microfront-end solution you’ve ever seen
  • Front-end Architecture: From Getting Started to Microfrontend