[toc]
1. What Is MFE(Micro Frontend)
When I first heard the concept of Micro Frontend, it made me feel it's a Buzzword, like Microservices.But as the pace of usage of that, I knew that's a powerful architecture in our daily development
The term Micro Frontends first came up in ThoughtWorks Technology Radar at the end of 2016.
The Micro Frontends official website defines the concept of micro frontends:
Techniques, strategies and recipes for building a modern web app with multiple teams that can ship features independently.
translate to Chinese:
构建一个现代web应用所需的技术、策略和方法,具备多个团队独立开发、部署的特性
translate to human language:
We can learn that the concept of micro frontends is extended from the concept of microservices . It abandons the large-scale monolithic approach and decomposes the front-end as a whole into small and simple blocks. These blocks can be independently developed, tested and deployed , while still Aggregate as a product to appear in front of customers. It can be understood that micro-frontend is an architectural style that aggregates multiple small front-end applications that can be delivered independently into a whole.
A few points worth noting:
- Micro-frontend is not a specific technology , but integrates technologies, strategies and methods, and may be displayed in the form of scaffolding, auxiliary plug-ins and specification constraints , which is a macro-level architecture . There are currently a variety of solutions for this architecture , all of which have advantages and disadvantages, but as long as the current business scenario is applicable, it is a good solution.
- Micro frontends are not constrained by technology stacks . The design of each micro-front-end solution is based on actual needs. If multiple teams use the react technology stack uniformly, there may be no requirements for the cross-technology stack use of the micro-frontend solution; if multiple teams use the react and vue technology stacks at the same time, the cross-technology stack requirements for the micro-frontend may be relatively high
2. From Monolithic Frontend to Micro Frontend
2.1 Monolithic Frontend Structure
As you can see, from the traditional Monolith structure to Microserve structure. Each Microservices in the backend operates independently of each other, each team can have its own deployment and development technology, communication can be achieved through various API interfaces, and various services can be connected to the frontend only by HTTP request.
Under such a structure, the frontend team's code still coexists in a Monolithic structure. When the website functions become more complex and the team grows stronger, the entire frontend structure will become more and more difficult to maintain, not to mention the advanced technology With each passing day, it is easy to generate legacy code, and if you want to update it, it will be troublesome.
2.2 Micro Frontend Structure
As someone slowly realized this kind of problem, the idea of Micro Frontends was put forward:
It is not difficult to find from the figure that Frontend's modules are divided into various teams, each team independently manages its own front-end and back-end services, has its own deployment environment and tech stack, low coupling between teams, and high cohesion among teams.
In addition, the front-end modules produced by each team must be able to be effectively "collaged" in the same SPA page, so that the user experience of the product is consistent with the original SPA.
2.3 The core idea of Micro Frontend
- Be Technology Agnostic : Each team can use its own technical structure to develop front-end modules, without interfering with other teams and without cost coordination.
- Isolate Team Code : Even if teams use the same technical framework, they should not share variables or state with each other. They should communicate with each other through a public API.
- Establish Team Prefixes : Use Prefix to avoid conflicts between CSS, Browser API, Web Events, Cookies or Local Storage.
- Favor Native Browser Features over Custom APIs : The complexity of integrating Micro Frontends is actually very high. When communication between each module is required, it is better to use Browser Native API as much as possible; if you really need additional communication methods (pub/ sub system), keep it as simple as possible.
- Build a Resilient Site : Enhance the stability of the website through SSI or PWA, and also have a usable presentation even when JS cannot be executed.
3. Pain points to implement Micro Frontend(how to)
3.1 Isolation
3.1.1 JS Isolation
Each Micro frontend has its own variables and functions, but when they are embed into portal, these variables and functions might conflict with portal. So we need a JS sandbox with individual scope to isolate each micro frontend. There are 2 classical approches to resolve this:
-
proxy sanbox(recommended, refer to use Proxy to implement sanbox ioslation)
snapshot sandbox
active() {
if (this.sandboxRunning) {
return;
}
this.windowSnapshot = {} as Window;
// iter will traverse target object, and then call the callback
// recording current window snapshot
iter(window, prop => {
this.windowSnapshot[prop] = window[prop];
});
Object.keys(this.modifyPropsMap).forEach((p: any) => {
window[p] = this.modifyPropsMap[p];
});
this.sandboxRunning = true;
}
inactive() {
this.modifyPropsMap = {};
iter(window, prop => {
if (window[prop] !== this.windowSnapshot[prop]) {
// recover the window
this.modifyPropsMap[prop] = window[prop];
window[prop] = this.windowSnapshot[prop];
}
});
this.sandboxRunning = false;
}
3.1.2 CSS Isolation
Same as JS isolation, class names of ids of css in each micro frontend may be conflict.
- BEM(prefix)
- css in js
- shadow dom
3.2 Low Coupling(how to load common dependencies)
3.3 Version Control
3.4 Communication
3.5 Hijack Route
3.6 etc...
4. Workaround of Micro frontend
4.1 iframe
In the module of the Team product, the other two modules can be loaded by embedding the iframe. Since the iframe has the characteristic of isolating the running environment, the modules of each team will not interfere with each other. Under the domain, we can use window.postMessage
.
<body>
<!-- in Team Product -->
<iframe width="100%" height="200" src="https://microfrontends-checkout.com/"></iframe>
<iframe width="100%" height="200" src="https://microfrontends-inspire.com/"></iframe>
</body>
Disadvantages:
There are many disadvantages of using iframe.
For example: because the application modules are separated, the shared dependent modules cannot be taken out, resulting in the page may be loaded at the same time. code; in addition, the presentation of the UI will become difficult to control, and if it also contains functions such as forms, it will be even more troublesome.
And iframe only resolve the isolation issue, if we do create a micro frontend, we might also need to pay more effort.
4.2 Web component
The same as iframe, web component is born with the js and css isolation, but it can not be compatible with all browsers. Moreover, when facing high complex componet or system, I think web component cannot handle this easily.
4.3 Single-SPA/Qiankun
Regarding single-SPA, it's only a scheduling of child application lifecycle. It only do 2 things:
- implement a life cycle, loading child application in load life cycle, and then hand over the control to child application, he doesn't care about the life cycle or statement of child application.
- monitoring the change of the url address. With the url change, some child applicaitons will be active, then go through the while life.
single-SPA hopes main application would be very very simple and lite, only need a index.html
and a main.js
is enough. Even webpack
it doesn't require
5. Module Federation
Multiple separate builds should form a single application. These separate builds should not have dependencies between each other, so they can be developed and deployed individually. This is often known as Micro-Frontends, but is not limited to that.
// todo pic MF的图 host remote
const HtmlWebpackPlugin = require("html-webpack-plugin");
const ModuleFederationPlugin = require("webpack/lib/container/ModuleFederationPlugin");
module.exports = {
// 其他webpack配置...
plugins: [
new ModuleFederationPlugin({
name: 'empBase',
library: { type: 'var', name: 'empBase' },
filename: 'emp.js',
remotes: {
app_two: "app_two_remote",
app_three: "app_three_remote"
},
exposes: {
'./Component1': 'src/components/Component1',
'./Component2': 'src/components/Component2',
},
shared: ["react", "react-dom","react-router-dom"]
})
]
}
字段名 | 类型 | 含义 |
---|---|---|
name | string | 必传值,即输出的模块名,被远程引用时路径为${name}/${expose}
|
library | object | 声明全局变量的方式,name为umd的name |
filename | string | 构建输出的文件名 |
remotes | object | 远程引用的应用名及其别名的映射,使用时以key值作为name |
exposes | object | 被远程引用时可暴露的资源路径及其别名 |
shared | object | 与其他应用之间可以共享的第三方依赖,使你的代码中不用重复加载同一份依赖 |