First, the characteristics of micro front end development
1. Technology independence: each development team can choose their own technology stack, which is not affected by other teams in the same project;
2. Business independence: Each deliverable can be used independently to avoid coupling with other deliverables;
Style isolation: Styles in each deliverable do not contaminate other components;
Native support: Each deliverable is free to use the native API, rather than requiring a packaged API. This fourth point is generally difficult to achieve.
What to do with the architecture micro front end
1. Comparison of micro-service platform and micro-front end:
A micro front end is an architecture, it’s not a technology. What do you need to do when you go to build a micro front end? We can figure out what to do by comparing it to a micro service.
Micro service | Micro front-end | note | |
---|---|---|---|
service | Independent services, such as transaction services | Applications or modules, such as navigation | Services don’t interact |
Service governance | Service registration/discovery/dependency management/tracing/degradation/flow limiting/logging/monitoring/o&M | Discover, route, authenticate, monitor, degrade, run, deregister, and aggregate applications | One/more systems are needed to handle some of the upper-level things uniformly |
Service communication | HTTP/RPC/middleware | eventBus/sharedWorker/BroadcastChannel/LocalStorage |
As you can see from the table above, when building a micro front end, you have at least two layers, a service layer and a service governance layer. The application layer is a specific application or module, while the service governance layer is a unified processing of some basic application problems, such as application discovery, routing, authentication, monitoring, degradation, running, deregistration, aggregation, and so on.
Architecture is technology agnostic.
2. Basic structure of micro front end:
The upper layer is the application layer, including N applications, and the lower layer is the application management layer, which is a main project.
- Centralized routing, service registry
- Identifying applications
- Design a life cycle
- Deployment and configuration automation
The main frame has the following functions:
- Application discovery and scheduling;
- Transition animation/log/report;
- Application isolation impact (Application 2 is not affected if application 1 fails to meet the requirements) /CSS isolation impact
- Application monitoring/degradation (need to be able to quickly switch to alternative solution when normal solution is not available)/authentication, etc.
- Communication mechanism between applications.
What if the main frame dies?
First, how to ensure that the main project does not fail (quality manpower, key Code Review, etc.);
The second is how to quickly detect (monitor) and repair after the main frame is hung (one is to provide backup for the main frame, and switch to the backup when the main frame is hung); Hot repair capability).
3. Common application scenarios of microservices
1. Break a large single app into multiple independent apps for seamless switching through navigation and dynamic loading.
2. Divide the content on a single page into different modules and give them to different teams to maintain.
How to implement the micro front end
It does two things — split and aggregate.
1. Common split methods:
- The large warehouse is split into separate modules, divided into different directories, but still in a large warehouse, with a unified build.
- The large repository uses Monorepo methodology to split multiple NPM packages and publish the NPM packages to the NPM repository or integrate them into the main project by means of Git submodules. You can build independently, but you can’t publish independently.
- The large repository can be split into multiple sub-repositories to build and deploy independent online services/applications, which can be aggregated into a complete application through iframe or WebComponent. This method can be built independently or published independently.
- The large warehouse is divided into several sub-warehouses, which are built to generate JS/CSS/manifest.json and other files, which are dynamically loaded in the main application and integrated into the main framework. This way you can either build independently or publish independently.
2. Common technical solutions:
1) Aggregation through iframe;
For some common UI components, repeated loading is still required. That’s the problem with iframe mode. Iframe nesting is a big problem; Iframe may have compatibility problems on mobile devices. Iframe blocks onLoad, occupies connection pools, and crashes multilevel nested pages.
The advantages of iframe are also obvious: low retrofit cost and fast rollout; In the sandbox model, each module is naturally isolated, and there is no need to consider style contamination and interaction between applications.
Demo in iframe mode:
constructor(props) {
super(props);
this.gotoApp = this.gotoApp.bind(this);
window.addEventListener('message'.this.handleMessage.bind(this));
}
state = {
currentApp: null
}
handleMessage(e) {
// Define a standard communication mechanism. For example, the data passed by the parent page contains fields such as type, data, etc
const { data } = e;
// Here type is defined as domReady, Load, Unload, openLink
if (data.type === 'openLink') {
// Open links in subpages
alert(data.data.url);
// Push URL into state
window.history.pushState({}, ' ', data.data.url); }}render() {
let { currentApp } = this.state;
return (
<div className="app">
<header className="app-header">
{ apps.map((app) => {
return (
<div className="item" onClick={()= > {this.gotoApp(app)}}></div>)})}</header>
<div className="content">
{currentApp ? <iframe src="currentApp.link" onError="{this.handleException}" border=""></iframe>}
</div>
</div>
);
}
handleException() {
// Handle exceptions, demotion schemes, etc
}
gotoApp(app) {
// This is where the transition animation can be displayed
// You can also add logs to record application usage
this.setState({
currentApp: app
})
}
urlParse() {
// Handle the URL module to solve the problem that the URL cannot be recorded
}
Copy the code
// App.js
export default class extends Compoent {
constructor(props) {
super(props);
}
state = {
currentApp: null
}
render() {
return (
<div className="app">
<header className="app-header">
<div onClick={()= > { openLink('/a/b') }}Learn React</div>
</header>
</div>)}}Copy the code
// Client.js // define an SDK that is responsible for the communication between parent and child pages // TODO: how to handle the communication between applications; How to solve sub-applications using a tag, Rather than through the agent to the main application of the let postMessage = function (data) {window. Top. Window. PostMessage (data, 'http://localhost:3001') } window.addEventListener('DOMContentLoaded', () => { postMessage({ type: 'domReady', data: { time: Date.now() } }) }) export function openLink(url) { postMessage({ type: 'openLink', data: { url: url } }) }Copy the code