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 toDEMO
Part 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.5qiankun
Feature:
- Based on the single-SPA package, provides a more out-of-the-box API.
- Technology stack independent, any technology stack applications can use/access, be it React/Vue/Angular/JQuery or other frameworks.
- HTML Entry access allows you to access micro applications as easily as using iframe.
- Style isolation ensures that styles do not interfere with each other between microapplications.
- JS sandbox to ensure no global variable/event conflicts between microapplications.
- 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:
- Development experience: Difficult to solve during development
iframe
Communication between and style problems, development difficulty. - User experience: Slow and
url
Out of sync. Browser refreshiframe url
Status lost, back forward button unavailable.
See Why Not Iframe for details
2 Application Scenarios
- History project maintenance and iteration (e.g
Angular. 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, changedwebpack
The 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.html
Page redirection
nginx.conf
- 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
- To solve
history
Mode 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:
webpack
Package configuration, the package file should beumd
- Child applications must be set up for cross-domain licensing
- The main application remembers to add mount containers, containers
id
orclass
Must be connected tocontainer
The same
FAQ
qiankun
Is the production environment available?
The answer is yes, production is available. Qiankun is ant Financial’s internal practice achievement sharing.
- How do you split the front-end application?
- According to the business
- According to the authority
- By upgrade frequency
- 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 upgrade
release
Launching 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