Principle and implementation of micro frontends Qiankun framework

principleimplementationmicrofrontendsqiankun

List of articles
Microfront (Micro-Frontends)qiankun Framework principle and implementation
1. qiankun brief introduction
2. qiankun Core design concept
3. Why not iframe
4. qiankun Characteristics of
5. Realization principle
(1) App load
(2)js Isolation
(3)css Isolation
(4) signal communication
Microfront (Micro-Frontends) actual combat (qiankun-vue-react)
1、 Base applications
a. Create a base application vue( belt router)
b. Foundation application installation qiankun
c. Base application introduction element
d. modify main.js
e. modify App.vue
2、 Microapplication vue
a. establish vue application ( belt router)
b. modify main.js
c. establish config file
d. modify router
3、 Microapplication react
a. establish react application
b. install yarn
c. install react-app-rewired
d. install react-router-dom
e. establish config
f. modify package.json
g. modify index.js
h. modify app.js

1.qiankun brief introduction
qiankun It's based on single-spa Micro front end implementation library of , It's designed to help you make it easier 、 Painless construction of a production usable micro front end architecture system .

qiankun Incubated from ant financial technology's unified access platform for cloud products based on micro front-end Architecture , After a batch of on-line application of full inspection and grinding , We extract its micro front-end kernel and open source it , I hope to help the community with similar needs to build their own micro front-end system more conveniently , At the same time, we hope that with the help of the community, we will qiankun Polished more mature and perfect .

at present qiankun Has served more than 200+ Online applications , In terms of ease of use and completeness , Absolutely trustworthy .

2.qiankun Core design concept
Simple
Because the main application and micro application can be technology stack independent ,qiankun For users, it's just like jQuery The library of , You need to call a few qiankun Of API The micro front-end transformation of the application can be completed . At the same time as qiankun Of HTML entry And sandbox design , Making the access of micro application like using iframe It's as simple as .

decoupling / Technology stack independent
The core goal of the micro front end is to split the giant application into several autonomous loosely coupled micro applications , and qiankun Many of our designs adhere to this principle , Such as HTML entry、 The sandbox 、 Communication between applications, etc . Only in this way can we ensure that micro applications really have Independent development 、 Independent operation The ability of .

3. Why not iframe
If you don't think about experience ,iframe Almost the perfect micro front end solution .

iframe The biggest feature is to provide browser native hard isolation scheme , Whether it's style isolation 、js Isolation is a problem that can be solved perfectly . But his biggest problem is that his isolation cannot be broken through , As a result, the context between applications cannot be shared , The development experience that comes with it 、 The problem of product experience .

In fact, this question has been mentioned in the previous article , Take it out here alone and review it .

1url Out of sync . Browser refresh iframe url State lost 、 The back and forward button doesn't work .
2UI Out of sync ,DOM Structures don't share . Imagine the lower right corner of the screen 1/4 Of iframe Here's a bullet frame with a mask layer , At the same time, we require the pop-up box to be displayed in the center of the browser , And a browser resize Center automatically when you move …
3 Global context is completely isolated , Memory variables are not shared .iframe Communication between internal and external systems 、 Data synchronization and other requirements , Main application cookie It should be transmitted to sub applications with different root domain names to achieve the effect of free login .
4 slow . Every time a child app enters, it's a browser context reconstruction 、 The process of reloading resources .

Some of them are easier to solve ( problem 1), We can turn a blind eye to some problems ( problem 4), But there are some problems that we can't solve ( problem 3) It can't even be solved ( problem 2), And these unsolvable problems will bring very serious experience problems to the product , In the end, we gave up iframe programme .

4.qiankun Characteristics of
be based on single-spa encapsulation , More out of the box API.
Technology stack independent , Any application of technology stack can be used / Access , Whether it's React/Vue/Angular/JQuery Or other frameworks .
HTML Entry Access mode , Let you access micro applications like using iframe It's as simple as .
Pattern isolation , Make sure that the styles between micro applications don't interfere with each other .
🧳 JS Sandbox isolation , Make sure that between micro applications Global variables / event No conflict .
️ Resource preload , Preload unopened micro application resources in browser idle time , Speed up micro application opening .
umi plug-in unit , Provides @umijs/plugin-qiankun for umi One click switch to micro front end architecture system .

5. Realization principle
(1) App load
qiankun A more complete application loading scheme is given , Provide npm plug-in unit import-html-entry, Allow to use html The file is the application entry , And then through a html The parser extracts... From the file js and css rely on , And pass fetch Download dependency

register app Writing :
```javascript
const MicroApps = [{
  name: 'app1',
  entry: 'http://localhost:8080',
  container: '#app',
  activeRule: '/app1'
}]
```
qiankun Will pass import-html-entry request http://localhost:8080, Get the corresponding html file , Resolve all internal script and style label , Download and execute them in turn , This makes application loading easier to use .import-html-entry The exposed core interface is importHTML

The core approach importHTML:
```javascript
export default function importHTML(url, opts = {}) {}
```
The specific process is as follows :
```json
1. Check if there is a cache , If there is , Return directly from cache
2. without , Through fetch download , And string
3. Template string based on regular expression template string based on regular expression Perform a template analysis , The main styles of the out - of - line script and the out - of - line task are the out - of - line script , Save in scripts and styles in
4. call getEmbedHTML, Download the outreach style , And replace it into the template , Make it an internal style
5. Return an object , This object contains the processed template , as well as getExternalScripts、getExternalStyleSheets、execScripts Wait for several core methods .
getExternalStyleSheets: Traverse styles Array , If it's inline style , Then return directly ; Otherwise, judge whether there is... In the cache , without , Through fetch To download , And cache .
getExternalScripts: Traverse scripts Array , Determine whether there are in the cache one by one , without , Through fetch To download , And cache .
execScripts: see js Isolation
```
(2)js Isolation
qiankun adopt import-html-entry, It can be done to html Analyze the entry , And get a method that can execute the script execScripts.qiankun When this interface is introduced , First, generate a for the application window Proxy object of , Then pass the proxy object as a parameter into the interface , To ensure... In the application js Will not affect the overall situation window Impact . because IE11 I won't support it proxy, therefore qiankun Isolate by snapshot policy js, The disadvantage is that it cannot support multi instance scenarios .

execScripts Method
```javascript
export function execScripts(entry, scripts, proxy = window, opts = {}) {}
```
execScripts To realize js Isolated is geval Called in function getExecutableScript function
```javascript
function getExecutableScript(scriptSrc, scriptText, proxy, strictGlobal) {
}
```
Put the analysis out of scriptText( Script string ) use with(window){} Wrap it up , And then put window.proxy Pass in as the first parameter of the function , therefore with Grammatical window It's actually window.proxy.

such , When executing this code , All similar to var name = ' Zhang San ’ Such statements add global variables name, It's actually mounted to window.proxy On , Not the real overall situation window On . When the application is unloaded , Corresponding proxy Will be cleared , Therefore, it will not lead to js Pollution .

If you use... In your app jquery, So this jquery The object will be mounted to window.proxy On .

If you write directly in the code window.name = ' Zhang San ’ To generate global variables , that qiankun You can't isolate js It's polluted .

Load one application at a time ,qiankun Just initialize such a proxySandbox Agent sandbox , Pass in the above execScripts Function .
```javascript
export default class ProxySandbox implements SandBox {
  ...
  constructor(name: string) {
    ...
    const proxy = new Proxy(fakeWindow, {
      set () { ... },
      get () { ... }
    }
  }
}
```
stay IE Next , because proxy Is not supported , therefore qiankun Second best , Snapshot strategy is adopted to realize js Isolation . Its general idea is , Before loading the application , take window Save all the properties on the ( Take a snapshot ); When the application is uninstalled , To restore window All properties on , This can also prevent global pollution . When more than one application exists at the same time ,qiankun It cannot be separated , therefore IE The snapshot policy under cannot support multi instance mode .

(3)css Isolation
at present qiankun It mainly provides two style isolation schemes , One is based on shadowDom Of ; The other is experimental , The idea is similar to Vue Medium scoped attribute , Add a special attribute to the root node of each child application , For all css Constraints on selectors .

The syntax for opening style isolation is as follows :
```javascript
registerMicroApps({
  name: 'app1',
  ...
  sandbox: {
    strictStyleIsolation: true
    // Experimental scheme ,scoped The way
    // experimentalStyleIsolation: true
  },
})
```
When you enable strictStyleIsolation when ,qiankun Will adopt shadowDom Style isolation , That is, create a node for the root node of the child application shadow root, All of the DOM Will form a tree shadow tree, The style of all nodes inside it is invalid for nodes outside the tree , All of them realize style isolation .

But this scheme is flawed . Because of some UI The framework may generate some pop-up boxes and mount them directly to document.body Next , At this time, due to separation from shadow tree, So its style will still pollute the whole world .

Besides qiankun Also exploring something similar to scoped Property , Can pass experimentalStyleIsolation To open . The strategy of this scheme is to add a specific random attribute to the root node of the child application , Such as :
```javascript
<div
  data-qiankun-asiw732sde
  id="__qiankun_microapp_wrapper__"
  data-name="module-app1"
>
```
Add... To the style :
```javascript
.app-main { 
  font size :14 px ; 
}
// ->
div[data-qiankun-asiw732sde] .app-main {  
  font size :14 px ; 
}
```
It doesn't support @ keyframes,@ font-face,@ import,@ page( That is, it will not be rewritten ).

(4) signal communication
Generally speaking , The communication before each application in the micro front-end should be as few as possible , And this depends on the reasonable splitting of the application . On the other hand , If you find that there is extremely frequent communication between two applications , So it is generally caused by unreasonable splitting , At this time, they often need to be combined into one application .

Yes , A small amount of communication between applications is inevitable .qiankun The official provided a brief plan , The idea is based on an overall globalState object . This object is created by the base application , It contains a set of variables for communication , And two methods for modifying variable values and monitoring variable changes :setGlobalState and onGlobalStateChange.

Base application initialization globalState:
```javascript
import { initGlobalState, MicroAppStateActions } from 'qiankun';

const initialState = {};
const actions: MicroAppStateActions = initGlobalState(initialState);

export default actions;
```
there actions The object is what we call globalState, That is, the global state . The base application can be passed when loading sub applications props take actions Pass to subapplication , The sub application can monitor the global state changes through the following statements : Subapplication :
```javascript
actions.onGlobalStateChange (globalState, oldGlobalState) {
  ...
}
actions.setGlobalState(...);
```
Besides , Base applications and other sub applications can also perform these two operations , So as to realize the sharing of global state , In this way, applications can communicate with each other .

qiankun Official website qiankun (umijs.org)

### Microfront (Micro-Frontends) actual combat (qiankun-vue-react)
1、 Base applications
Equivalent to socket , It could be simple html It can also be any front-end application

The base application can have its own routing , You can also reference sub applications

a. Create a base application vue( belt router)
```javascript
vue create qiankun-base
```
b. Foundation application installation qiankun
install qiankun
```javascript
npm i qiankun -S
```
c. Base application introduction element
elementUI
```javascript
npm i element-ui -S

import Vue from 'vue';
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
import App from './App.vue';

Vue.use(ElementUI);

new Vue({
  el: '#app',
  render: h => h(App)
});
```
d. modify main.js
```javascript
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
// introduce qiankun
import {registerMicroApps,start} from 'qiankun';
// register app list
const apps = [
  {
    name: 'vueapp',
    entry: '//localhost:9999',// Automatic loading , analysis js, Dynamic execution , Sub applications need to solve cross domain problems
    container: '#vue',// Subapplication mount element
    activeRule: '/vue',// Activation rules , When accessing this rule , mount
    props: {// Parameters passed
      a: 1
    }

  },
  {
    name: 'reactapp',
    entry: '//localhost:20000',// Automatic loading , analysis js, Dynamic execution , Need to solve cross domain problems
    container: '#react',
    activeRule: '/react'
  }
]
registerMicroApps(apps);// Here you can select parameters to set the life cycle method as required
start();

Vue.use(ElementUI);

new Vue({
  router,
  render: h => h(App)
}).$mount('#app')
```
e. modify App.vue
```javascript
<template>
  <div><!-- id -->
    <el-menu :router="true" mode="horizontal">
      <!-- The base can use its own routing -->
      <el-menu-item index="/">Home</el-menu-item> 
      <!-- The base can introduce other sub applications -->
      <el-menu-item index="/vue">vue application </el-menu-item>
      <el-menu-item index="/react">react application </el-menu-item>
    </el-menu>
    <router-view/>
    <div id="vue"></div>
    <div id="react"></div>
  </div>
</template>
<style>
</style>
```
#### 2、 Microapplication vue
a. establish vue application ( belt router)
```javascript
vue create qiankun-vue
```
b. modify main.js
```javascript
import Vue from 'vue'
import App from './App.vue'
import router from './router'

let instance = null;
function render() {
  instance = new Vue({
    router,
    render: h => h(App)
  }).$mount('#app')// Or render it to your own html in , The base will take the html mount
};
// Determine whether the sub application runs independently
if (!window.__POWERED_BY_QIANKUN__) {// Run independently by default
  render();
}
if (window.__POWERED_BY_QIANKUN__) {
  __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
}
// Expose three life cycle hooks
export async function bootstrap(props){};

export async function mount(props){
  console.log(props)
  render()
};
export async function unmount(props){
  instance.$destroy()
};
```
c. establish config file
```javascript
module.exports = {
    devServer: {
        port:9999,// Set the boot port
        headers:{
            'Access-Control-Allow-Origin':'*'// Access control , Allow all
        }
    },
    configureWebpack:{
        output:{
            library:'vueApp',
            libraryTarget:'umd'
        }
    }
}
```
d. modify router
```javascript
...

const router = new VueRouter({
  mode: 'history',
  base: 'vue',// The default path
  routes
})

...

```
#### 3、 Microapplication react
a. establish react application
```javascript
npx create-react-app my-app
```
b. install yarn
```javascript
npm install -g yarn
```
c. install react-app-rewired
```javascript
yarn add react-app-rewired
```
d. install react-router-dom
```javascript
yarn add react-router-dom
```
e. establish config
Configure application startup 、 Packaging and cross domain parameters
config-overrides.js
```javascript
module.exports = {
    webpack:(config)=>{
        config.output.library = 'reactApp';
        config.output.libraryTarget = 'umd';
        config.output.publicPath = 'http://localhost:20000/';
        return config;
    },
    devServer:(configFunction)=>{
        return function(proxy,allowedHost){
            const config = configFunction(proxy,allowedHost);
            config.headers = {
                'Access-Control-Allow-Origin':'*'
            }
            return config;
        }
    }
}
```
f. modify package.json
```javascript
...
  "scripts": {
    "start": "react-app-rewired start",
    "build": "react-app-rewired build",
    "test": "react-app-rewired test",
    "eject": "react-app-rewired eject"
  },
  ...

```
g. modify index.js
```javascript
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')
  );
};

if(!Window.__POWERED_BY_QIANKUN__){
  render()
}
export async function bootstrap(props){};

export async function mount(props){
  render()
};
export async function unmount(props){
    ReactDOM.unmountComponentAtNode(document.getElementById('root'))
};
```
h. modify app.js
```javascript
import logo from './logo.svg';
import './App.css';
import {BrowserRouter,Route,Link} from 'react-router-dom'


function App() {
  return (
    <BrowserRouter basename='/react'>
      <Link to='/'> home page </Link>
      <Link to='/about'>about</Link>
      <Route path='/' exact render={()=>(
            <div className="App">
            <header className="App-header">
              <img src={logo} className="App-logo" alt="logo" />
              <p>
                Edit <code>src/App.js</code> and save to reload.
              </p>
              <a
                className="App-link"
                href="https://reactjs.org"
                target="_blank"
                rel="noopener noreferrer"
              >
                Learn React
              </a>
            </header>
          </div>
      ) 
      }>
      </Route>
      <Route path='/about' render={()=>
            <h1> This is a react About the page </h1>
      }>
      </Route>
    </BrowserRouter>
  );
}

export default App;
```
i. start-up
```javascript
npm start
```

版权声明:本文为[qq_ thirty-nine million six hundred and seventy thousand and tw]所创,转载请带上原文链接,感谢。 https://qdmana.com/2022/174/202206231403025741.html