Knowledge Reserve

What is a microfront-end (or microfront-end architecture)?

define

Techniques, strategies and recipes for building a modern web app with multiple teams that can ship features independently. — Micro Frontends

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

To put it simply, large enterprise-level Web applications are divided into small projects, namely “micro-applications”, and then micro-applications are integrated in series through the main application to achieve the display of all application pages.

Features & Advantages

  • Stack independent

The main framework does not limit the technology stack of access applications, and microapplications have full autonomy

  • Independent development and deployment

The microapplication repository is independent, and the front and back ends can be independently developed. After deployment, the main framework can automatically complete synchronous update

  • The incremental upgrade

In the face of a variety of complex scenarios, it is usually difficult to upgrade or reconstruct the existing system completely, and the micro front end is a very good means and strategy to implement the gradual reconstruction

  • Independent run time

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

The core value of

The micro front-end architecture is designed to solve the problem of unmaintainable application in a relatively long time span, when a single application evolves from a common application to a Frontend Monolith due to the increase and change of the number of people and teams involved. This type of problem is especially common in enterprise Web applications.

Why micro front end?

When developing an enterprise-level project, the life cycle will be relatively long. Usually, with the increase in demand and the continuous improvement of functions, our project will become bigger and bigger. At this time, our application will become a monolith application. Consequently, the project is difficult to maintain, and the cost of getting started is high.

Disadvantages of megalithic application are summarized as follows:

  • Large project reference dependent package, packaging slow online;

  • In the early stage of the project, the code is difficult to maintain and the product iteration is difficult;

  • ToB applications are difficult to quickly respond to customer customization requirements.

Therefore, when we have a relatively independent and complete new requirement, it is often developed by a new team, and we hope to support independent development and deployment, and finally expect to have access to the original application, and elegant integration with the original application.

According to the Not Iframe?

After establishing the micro front-end architecture, iframe is the first thing that comes to mind when we start to think about implementation solutions. Then we can take stock of the advantages and pain points of IFrame.

Advantages (Isolation)

Js and CSS isolation, no need to consider JS and CSS conflicts between microapplications and main applications

Use pain points (hard-to-break isolation)

  1. For stateless urls, the main application browser refreshes the IFrame URL state, and the back forward button cannot control the IFrame.
  2. The UI is not synchronized and the DOM structure is not shared. Imagine an iframe with a mask layer in the bottom right corner of the screen, and we want it to be centered in the browser and automatically centered when the browser resize.
  3. The global context is completely isolated and memory variables are not shared. Iframe internal and external system communication, data synchronization and other requirements, the cookie of the main application should be transparently transmitted to the sub-applications with different root domain names to achieve the effect of free registration.
  4. Slow. Each sub-application is a process of browser context reconstruction and resource reloading.
  5. The iframe height cannot be self-adaptive.

Problem solution

  • Problem 1: Listening for URL changes, JS controls the URL of iframe
  • Problem 2: It can’t be solved unless a “popbox UI data protocol” is implemented through communication between parent and child applications, and the parent application displays the popover.
  • Problem 3: Parent-child application communication.
  • Problem 4: You can turn a blind eye and leave it unresolved
  • Problem 5: Unsolvable

These pain points are very troublesome to solve, and if they are forcibly solved, they will lead to high coupling degree of parent-child application, which makes developers unable to focus on business logic and brings serious experience problems to the product, and ultimately leads to the abandonment of iframe solution.

What is “Qiankun”?

Following the iframe mentioned above, is there a relatively painless microapplication solution?

Yes, it was Qiankun. Qiankun introduction

Introduction to the

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.

The characteristics of

  • 📦 provides a more out-of-the-box API based on a single-SPA package.

  • 📱 technology stack independent, any technology stack applications can use/access, be it React/Vue/Angular/JQuery or other frameworks.

  • 💪 HTML Entry access, let you access micro applications as easy as iframe.

  • 🛡 style isolation to ensure that styles do not interfere with each other in microapplications.

  • 🧳 JS sandbox to ensure that global variables/events do not conflict between microapplications.

  • ⚡️ Resource preloading: Preloads unopened micro-application resources in idle time of the browser to speed up the opening of micro-applications.

  • 🔌 UMI plug-in, providing @umiJS/plugin-Qiankun for UMI applications to switch to a micro front-end architecture system with one click.

Core API

In fact, qiankun has very few apis, and it only takes 3 minutes to get. The following two scenarios are used to illustrate:

You can also view the full documentation here: qiankun.umijs.org/zh/api

Scenario 1: Route activation

import { registerMicroApps, start } from 'qiankun'; RegisterMicroApps ([{name: 'react app', // the name of the microapps must ensure unique entry between microapps: Container: '#yourContainer',// the container node selector or Element instance of the microapp activeRule: }, {name: 'vue app', entry: {scripts: ['//localhost:7100/main.js'] }, container: '#yourContainer2', activeRule: '/yourActiveRule2', }, ]); start(); // Start qiankun. There are optional parameters for prefetch and sandboxCopy the code

Scenario 2: Manual loading

import { loadMicroApp } from 'qiankun'; LoadMicroApp ({name: 'app', // the name of the microapp. Each microapp must have a unique entry: '//localhost:7100', // the entry of the microapp, indicating the access address of the microapp. '#yourContainer', // the container node selector or Element instance of the micro-application});Copy the code

As can be seen from the above, in the process of using Qiankun, it is necessary to tell Qiankun what is the name of the application to load, where to request to the page, and which element of the main application.

Actual Project

Build a micro front end project based on Qiankun

Next, it explains how to build the project from the two aspects of main application and sub-application respectively. It should be noted that the main application and sub-application can choose different technology stacks, which does not affect the construction of micro-front-end architecture, as follows:

The main application

1. Install qiankun

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

2. Register microapplications with the main application

import { registerMicroApps, start } from 'qiankun';

registerMicroApps([
  {
    name: 'react app', // app name registered
    entry: '//localhost:7100',
    container: '#yourContainer',
    activeRule: '/yourActiveRule',
  },
  {
    name: 'vue app',
    entry: { scripts: ['//localhost:7100/main.js'] },
    container: '#yourContainer2',
    activeRule: '/yourActiveRule2',
  },
]);

start();
Copy the code

Or manually load a microapplication

import { loadMicroApp } from 'qiankun';

loadMicroApp({
  name: 'app',
  entry: '//localhost:7100',
  container: '#yourContainer',
});
Copy the code

For example, loadMicroApp is executed directly in the lifecycle of a component to introduce a microapplication, as shown in the following example:

import { FC, useState, useEffect } from 'react';
import { loadMicroApp } from 'qiankun';

const MicroAppA: FC = () => {
    const [app, setApp] = useState({})
    useEffect(() => {
        const a = loadMicroApp({
            name: 'social-relationship',
            entry: '//localhost:3001',
            container: '#reactContainer',
        });
        setApp(a);
        return () => {
            (app as any).unmount && (app as any).unmount();
        }
    }, [])

    return <div>
        <div id="reactContainer"></div>
    </div>
}

export default MicroAppA
Copy the code

Microapplications (React Project)

1. Create projects

npx create-react-app my-app
cd my-app
npm start
Copy the code

Introduce react-router-DOM and write your own business page code.

2. Add public-path.js to the SRC directory

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

⚠️ Note: If ts is used, add a line comment to avoid build errors.

  • __POWERED_BY_QIANKUN__ indicates whether it was launched with a microservice architecture. If it was launched from the master app and accessed a child app, this parameter is true.
  • **__INJECTED_PUBLIC_PATH_BY_QIANKUN__** represents the publicPath injected by the master app publicPath, which is an entry configured in the master app code. After assignment, the main application entry can be obtained from __webpack_public_path__; (ps: this value is used when setting the SRC attribute on an iframe page.)

3. Set the base for the route in history mode

<BrowserRouter basename={window.__POWERED_BY_QIANKUN__ ? '/app-react' : '/'}>
Copy the code

⚠️ Note: the /app-react route needs to have the same protocol with the main application. If the main application wants to display micro-applications under the /app-react route, it needs to be set as follows:

In the main application, the path /app-react/koc is defined carefully, which is completely dependent on the micro application. The micro application must have a basename and /koc route. A master app should not use the /app-react/test path to match and display a microapp’s/koC;

4. Modify the index.js file

To prevent root ID #root from colliding with the rest of the DOM, you need to limit the scope of the search.

Several lifecycle hook functions are exposed when connected to qiankun, and direct render when not used.

import './public-path'; import React from 'react'; import ReactDOM from 'react-dom'; import App from './App'; function render(props) { const { container } = props; ReactDOM.render(<App />, 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('[react16] props from main framework', props); render(props); } export async function unmount(props) { const { container } = props; ReactDOM.unmountComponentAtNode(container ? container.querySelector('#root') : document.querySelector('#root')); }Copy the code

5. Modify webPack configuration

Install the plugins @rescripts/cli, and of course choose other plugins such as react-app-Rewired.

npm i -D @rescripts/cli
Copy the code

.rescriptsrc.js is added to the root directory, the name of the build exit and some parameters are configured, and the proxy is configured to allow cross-domain.

const { name } = require('./package'); module.exports = { webpack: (config) => { config.output.library = `${name}-[name]`; config.output.libraryTarget = 'umd'; config.output.jsonpFunction = `webpackJsonp_${name}`; config.output.globalObject = 'window'; return config; }, devServer: (_) => { const config = _; config.headers = { 'Access-Control-Allow-Origin': '*', }; config.historyApiFallback = true; config.hot = false; config.watchContentBase = false; config.liveReload = false; return config; }};Copy the code

Modify the package. The json:

-   "start": "react-scripts start",
+   "start": "rescripts start",
-   "build": "react-scripts build",
+   "build": "rescripts build",
-   "test": "react-scripts test",
+   "test": "rescripts test",
-   "eject": "react-scripts eject"
Copy the code

Microapplications (VUE Project)

The transformation method of VUE project can be referred to the official website article:

Qiankun.umijs.org/zh/guide/tu…

Microapplication isolation solution

Style isolation

background

The initial problem we encountered when connecting to the main application was that the micro application and the main application used antD or the same class name. There was a problem. When loading the child application, the main application style was affected.

thinking

So when using microapplications, we need to think about style isolation, and the three different scenarios we can think about, and the expectations should be as follows:

  • The main application style does not affect a microapplication style
  • One microapplication style cannot affect the main application style
  • The style of one microapplication cannot affect the style of other microapplications

Of course, the issue of style isolation was also taken into account in the design of Qiankun and some hints were provided. The official document explains this as follows:

Official website link: API description

plan

Let’s consider a simple single-instance scenario to see what these configuration scenarios can do:

The value of the sandbox

The effect

The problem

True (default)

Isolation between sub-applications

Primary and child applications are not isolated

{ experimentalStyleIsolation: true }

Isolation between sub-applications + Sub-applications does not affect the primary application

(Child application styles add special selectors to limit the scope of the styles.)

The master application affects the child application

The main application has layout: 10px! Important, the subapplication also has this layout class name.

{ strictStyleIsolation: true }

The sub-applications are isolated and the host and sub-applications do not affect each other

(Shadowdom-based implementation wraps the content inside the sub-application container into a ShadowDOM)

  1. Other problems will be introduced, and the official website has not provided a complete transformation method;

  2. Developers’ proficiency with ShadowDOM is limited and risky

Considering the above points, our final solution is: ANTD with custom prefix, and other styles are developed by CSS Module

Antd Custom prefix

The default prefix of ANTD is “ANTD”. If the main application and its children use this UI framework, there will be mutual influence, so we change the prefix of each child application to distinguish and avoid influence.

Here’s how to customize antD prefixes:

  1. We installed Craco (a community solution for custom configuration of create-React-app, recommended by ANTD) and changed the scripts properties in package.json.

    $ yarn add @craco/craco

    /* package.json */ “scripts”: {

    • “start”: “react-scripts start”,
    • “build”: “react-scripts build”,
    • “test”: “react-scripts test”,
    • “start”: “craco start”,
    • “build”: “craco build”,
    • “test”: “craco test”,

    }

  2. Then create a craco.config.js file in the project root directory to modify the default configuration.

    /* craco.config.js */ module.exports = { // … };

  3. Custom themes require less variable overrides similar to those provided by less-Loader. We can introduce craco-less to help load less styles and modify variables by changing the SRC/app.css file to SRC/app.less and then changing the style reference to less file.

    /* src/App.js */

    • import ‘./App.css’;
    • import ‘./App.less’;

    /* src/App.less */

    • @import ‘~antd/dist/antd.css’;
    • @import ‘~antd/dist/antd.less’;
  4. Install craco-less and modify the craco.config.js file as follows.

    $ yarn add craco-less

    const CracoLessPlugin = require(‘craco-less’);

    module.exports = { plugins: [ { plugin: CracoLessPlugin, options: { lessLoaderOptions: { lessOptions: { modifyVars: { ‘@primary-color’: ‘#1DA57A’ }, javascriptEnabled: true, }, }, }, }, ], };

Of course, in addition to @primary-color, you can also modify the configuration of other less variables, as described in the ANTD documentation.

Css module using

Css Module features can be introduced in each component of the Css style file, only for the current component rather than the global effect, the principle is that when parsing will enter the module Css encapsulated in a hash value class name, the class name is unique also to achieve the isolation between modules;

What is a CSS Module? & How to use it?

www.ruanyifeng.com/blog/2016/0…

Js isolation

Currently, there is no case where variables within each child application pollute each other. The child application does not have its own handling of variables such as Window, but some scenarios take the value of the main application Window. In case of contamination, it has been suggested to take a snapshot of the window in the child application’s loaded hook and then reassign the value in the unmounted hook to achieve isolation.

Related articles

  • Micro Frontends

  • Micro Frontends from martinfowler.com

  • Probably the most complete microfront-end solution you’ve ever seen

  • Core values of the micro front end

  • Qiankun introduction

  • Why Not Iframe

  • Web Components

  • GrowingIO platform integration application front-end development guide