What is a micro front end
Let me give a more precise definition of a micro front end. A micro front end is an aggregation of several small front end applications that are separate from each other but run in aggregation.
One of the core things is decoupling, decoupling at the project level.
In the current front-end field, single-page application (SPA) is one of the most popular project forms. As time goes by and the application functions become more and more rich, this stupid thing will become more and more complex, and will become a so-called monolith system, with high development cost and high mental loss for engineers. For example xiao ge told me that we this CMS on some places dare not change, change do not know which is broken, pull a launch the whole body. Or, for example, if the CMS develops two requirements at the same time under the same schedule, then both development and testing will be troublesome. And the earlier the micro front end starts, the better, when it really becomes a monolith system is hard to break down.
One function brought on this basis is the replacement of technology stack. For example, CMS uses VUE 2.x and now wants to use the newly released VUE 3. If large-scale reconstruction is carried out, the cost is very high. After all, we are decoupled from each other at the project level, so you can use them however you like.
The point of the micro front end is to break these massive applications apart and then decouple them so that each part can be maintained and deployed separately to improve efficiency.
Microfront-end solution
Probably the following
plan | describe | advantages | disadvantages |
---|---|---|---|
MPA + Route forwarding | Nginx configates location to realize the mapping of different paths to different applications, such as www.abc.com/app1 corresponding to APP1 and www.abc.com/app2 corresponding to APP2. This scheme itself is not the transformation of the front-end level, but the configuration of operation and maintenance. | Simple, fast and easy to configure | Jumping between applications triggers the browserThe refresh , the impactexperience Jump between applications Loading time A longDoes not support Multiple applications on the same screen |
Nested iframe | The parent application is a single page, and each child application has an iframe nested. The parent and child can communicate in postMessage or contentWindow mode | Simple implementation, the sub application between their own sandbox, natural isolation, mutual influence | Intercomponent communication .cookie The limits ofui Out of sync, for exampledialog Unable to fill the full screenForward and backward keys Can’t use |
Web Components | Each sub-application needs to use pure Web Components technology to write Components, which is a new set of development mode | Each sub-application has its own script and CSS and can be deployed separately | High cost for historical system transformationSubapplication communication More complex and easy to step pit |
Combined application route distribution | Each child application is built and deployed independently. At runtime, the parent application performs routing management, application loading, startup, uninstallation, and communication mechanisms | Pure front-end transformation, good experience, no perception switch, sub-applications isolated from each other | Intrusive development Need to solve the sub-application style Conflict,The variable object Pollution,Communication mechanism Such as technical point |
There is no level of technical solutions, only pros and cons.
Finally, we decided to use this combined application routing distribution, which I like to call pedestal micro front end for short, probably because it is more balanced, better experience, more controllable, and more solutions.
Process for the master application to load child applications
For example, enter log.feihua100.com/course-mana…
- Main application loading (web request, JS execution, page rendering)
2. Main application basisrouting
Get the child application name:course-manage
{
name: 'course-manage',}Copy the code
- Obtained by the main application
course-manage
The configuration of the
{
name: 'course-manage'.entry: process.env.NODE_ENV === 'development' ? '//localhost:7799/' : '/course-manage/'.activeRule: '/course-manage'.// The div mounted by the child application
container: '#subapp-viewport'.props: {
routerBase: '/course-manage',
store
}
}
Copy the code
- The primary application loads the child application
course-manage
Static resources (HTML, CSS, JS)
- Child application receiver
routing
. The route in the address bar matches the corresponding routevue
File, start renderingcourse-manage
The application incontainer
(byid
To find thediv
)
Micro front end implementation principle
The essence of micro-front-end is to deal with front-end applications and the relationship between applications. Then, to further implement a micro-front-end framework, three core elements will be involved:
-
Sub-application loading;
-
Runtime isolation between applications;
-
Communication between applications;
We will explain the above three points by explaining the API and source code of Qiankun, ant Financial open source stable micro front-end framework
Qiankun can be considered as the product of the secondary development by the combination of single-SPA and import-HTmL-Entry libraries
RegisterMicroApps method
The first is the registerMicroApps method, one of the core apis of Qiankun
This function registers the child application and, when the child application is activated, creates a run sandbox that calls different lifecycle hook functions at different stages.
It receives an array, which is the configuration of the child application.
In fact, what registerMicroApps basically does is deduplicate the array, iterate through the array and pass the configuration of each child application to registerApplication, the core function that registers the child application in single-SPA. As we mentioned, part of the core of Qiankun is single-SPA.
The input parameters to registerApplication are
-
Name – Name of the sub-application
-
ActiveRule – Activation rules for child applications
-
Callback-activerule callback when activated
-
Props – Data that the main application needs to pass to the child application
The registerMicroApps method is well understood as a way to register child applications with the master application, followed by another core method
The start method
The start function initializes some global Settings and then starts the application
The source code shows that Qiankun supports some prefetch preloading.
Then we can see that the global configuration is stored in the frameworkConfiguration, and a polyfill operation is performed. Let’s look at the details of this method:
From this we can see that the sandbox content is done by window.Proxy, if not by snapshot.
Finally, the start application is actually using the single-SPA start method.
LoadApp method
In the registration and initialization process above, we know that a callback will be executed when the current URL matches activeRule
The core is the return of the loadApp method, so this is the core method of the runtime, how do we load the child when we match the child, so let’s look at the loadApp method, okay
It’s too long to watch
This is the definition and configuration of some variables. Line 266 calls importEntry, which is the core method of the import-html-entry library
ImportEntry and importHTML methods
Let’s look at the importEntry method first
We can see that the core importHtml method is called directly when entry is a string, and then a chunk of it is returned when entry is configured, which can be approximated as an extension of the importHtml method. So it’s almost like the importHtml method is called. Let’s look at the importHtml method.
The fetch method, the publicPath method, and the template method can all be customized
-
We see that our entry is requested directly using fetch, is that why Qiankun needs sub-applications across domains
-
And then it returns an HTML template to string it
-
The default getPublicPath is the upper-level route to get the entry. If the static resource address is not the upper-level route to get the entry, you need to customize the getPublicPath method
-
Then there is the processTpl method, which parses all the JS scripts from a messy HTML template — including the outgoing address and the inline code block. The addresses of all link tags are the style addresses. There is no inlining, because the styles are converted to the inline style.
– template: indicates the script that has been processed. The link and script tags are commented out
HTTP address | – scripts: [script code block],
-styles: [HTTP address for styles]
– Entry: Indicates the address of the entry script, either the SRC of the script marked with entry or the SRC of the last script tag
This method is actually very interesting, can learn some link and script writing, but too tedious to talk about.
-
To process an HTML template using the getEmbedHTML method is to use fetch to remotely load all external styles, then replace the corresponding external styles with inline styles, and finally return the processed HTML template.
-
The final return is an object with the following attributes:
– template: indicates the processed HTML template
– assetPublicPath: indicates the static resource address
– getExternalScripts: Method of getting the previously parsed script array
-getexternalStylesheets: Method to get the array of stylesheets parsed earlier
– execScripts: execute all JS script files in this template file and specify the scope of the script. – proxy object
The section above can be considered as sub-application loading. This section explains the principle of sub-application resource loading, followed by the section of runtime isolation between applications.
Back to the loadApp method
Then there is the singleton pattern, where the new child application mount behavior starts after the old child application is unmounted.
Then a section of sandbox mode configuration, will talk about the implementation principle.
Next, the HTML of the sub-application was processed by templates and other methods and mounted in the container configured in the main application. It can also be seen here that qiankun was equipped with a loading mechanism of the sub-application.
The sandbox mechanism
The application of this part we have in front of the child after HTML processing in the application of our Lord in the container, and we know that much of the front page on the operation of the main body is the js, and js run most based on our window object, but when we are the main application and the application of parallel, or even more child applications in parallel, Our Window objects are likely to be contaminated with each other.
The qiankun project used a sub-application state management solution, js sandbox, in ProxySandbox, LegacySandbox and SnapshotSandbox.
SnapshotSandbox
The first is to use the SnapshotSandbox sandbox as a class when the window.Proxy property is not supported in earlier versions of browsers
attribute | meaning |
---|---|
name |
The name of the sandbox |
proxy |
The proxy object, here iswindow object |
sandboxRunning |
Whether the current sandbox is running |
windowSnapshot |
Window Status Snapshot |
modifyPropsMap |
The sandbox was modified during operationwindow attribute |
active |
Activate the sandbox and start it when the child application is mounted |
inactive |
Close the sandbox and start it when the child application is uninstalled |
constructor |
Constructor to create a sandbox environment |
There are two main methods of SnapshotSandbox, which are the implementation of its core functions.
The first is to activate the active method of the sandbox, which takes a snapshot of the window and restores the state of the sandbox if it has previously stored state:
The other is to disable the sandbox method inactive, which takes a snapshot of the Window object and records the state change of the sandbox:
The SnapshotSandbox sandbox uses snapshots to isolate the state of window objects. In contrast to ProxySandbox, the SnapshotSandbox will pollute the Window object during child application activation, which is a backward compatibility scheme for browsers that do not support the Proxy property.
LegacySandbox
Then there is the LegacySandbox sandbox, which as a class lets examine its properties
attribute | meaning |
---|---|
addedPropsMapInSandbox |
Global variables added during sandbox forClose the sandbox To restore the global state |
modifiedPropsOriginalValueMapInSandbox |
Global variable updated during sandbox, used forClose the sandbox To restore the global state |
currentUpdatedPropsValueMap |
Keeps a map of updated (new and modified) global variables used inActivate the sandbox To restore the independent state of the sandbox |
name |
The name of the sandbox |
proxy |
Proxy object, which can be understood as a child applicationglobal/window object |
sandboxRunning |
Whether the current sandbox is running |
active |
Activate the sandbox and start it when the child application is mounted |
inactive |
Close the sandbox and start it when the child application is uninstalled |
constructor |
Constructor to create a sandbox environment |
In fact, since LegacySandbox serves the singleton pattern, its core implementation is largely similar to that of SnapshotSandbox. Let’s look at the Active and Inactive methods.
In fact, we can see that this is also in the form of a snapshot. The difference is that window.Proxy is used as a Proxy, so it can listen on get and set to change the record:
We can see from the source code, in fact, LegacySandbox mode is actually a direct operation of the Window object, but there is no pollution between the child applications, and between the parent applications will cause window pollution.
The proxy object is the parameter used by the execScripts method above, which specifies the context in which JS is executed.
Finally, take a look at multi-instance sandbox ProxySandbox
ProxySandbox
Again, let’s look at the attributes in the class:
attribute | meaning |
---|---|
updatedValueSet |
Keep track of the updated values in the sandbox, which is a separate state pool for each child application |
name |
The name of the sandbox |
proxy |
Proxy object, which can be understood as a child applicationglobal/window object |
sandboxRunning |
Whether the current sandbox is running |
active |
Activate the sandbox and start it when the child application is mounted |
inactive |
Close the sandbox and start it when the child application is uninstalled |
constructor |
Constructor to create a sandbox environment |
ProxySandbox does not operate directly on the Window object. Instead, ProxySandbox copies a fakeWindow object as a proxy:
There is no need to restore or reset Windows since they are not directly operated:
While updatedValueSet will synchronize the records of operations on fakeWindow:
So I have a question, since parent and child applications don’t pollute Windows with each other, does this update Valueset make any sense?
In summary, ProxySandbox is the most complete sandbox mode, completely isolating the state of the master application.
Above is the section about Windows in runtime isolation between applications. We know that we have some side effects that need to be isolated from each other, such as event listeners, timers, etc.
Listening to hijack
The main content is in patchAtMounting method
Here are three types of monitoring hijacking, respectively:
-
patchTimer
-
PatchWindowListener (Window event listener hijacking)
-
PatchHistoryListener (Route Listener)
patchTimer
The first is the patchTimer hijacking:
The idea here is relatively simple, is to do a layer of proxy timer, let’s find someone to explain the principle here?
The main thing is to create a state pool and maintain the ID so that it can be released uniformly when uninstalling child applications.
The principles of patchWindowListener and patchHistoryListener and patchTimer are not explained in detail
There are some other side effects of loading and unloading that I won’t go into.
Style isolation
Above are the contents of THE JS sandbox isolation, let’s talk about the CSS style isolation method.
Qiankun provides two types of CSS style isolation:
-
Shadow DOM
-
CSS Scoped
Let’s do it separately
Shadow DOM
The Shadow DOM allows you to attach a hidden DOM tree to a regular DOM tree — it starts with a Shadow root node, and underneath that root node can be any element, just like a normal DOM element. The hidden DOM style is completely isolated from the rest of the DOM, similar to the style isolation effect of iframe.
I won’t go into the details, it’s not interesting, and I’ll talk about why it’s not interesting
CSS Scoped
Next up is CSS Scoped, which I named Experimental Sandbox, adding suffix tags to all styles of sub-applications such as div[data-Qiankun-Microname].
At the same time, turn the CSS into a selector of this form, and you can make a CSS sandbox.
However, neither of these two methods can solve the problem of popovers. Popovers are mostly bound to the root node, which is to jump out of the sandbox, resulting in a problem with the style of popovers.
At this point we can manually tie the popover to the child application to solve this problem.
If we don’t use CSS sandboxes, can we use Webpack to solve the problem of style isolation?
Remote Patch request
All we’ve talked about above is the mount and unmount life cycle, but we actually have some style files or JS files that append to the page using remote requests, and there’s another layer of isolation.
The qiankun solution is exactly the same as some of these: Augmenting the appendChild and insertBefore methods and adding some additional logic to determine whether link, style, and script elements are inserted in the main or microapp depending on whether they are children, and hijacking the addition of script tags. Supports remote loading of scripts and setting the execution context of scripts, also known as Proxy. I’m not going to get into the code because it’s complicated.
That concludes the section on runtime isolation between applications.
Communication between applications
The last one is about inter-application communication, which can be completed through the API of Qiankun. It is the content of actual combat. You can see the scheme in the article of enterprise-class micro front-end combat.
conclusion
This sharing is quite long, and the general process is as follows
-
What is a micro front end
-
The advantages and disadvantages of micro – front-end schemes are compared
-
Describes the micro front end solution we use
-
This paper describes a process of main application loading sub-application in our scheme
-
Finally, through the analysis of the library of The Qiankun UMI group, the principle is simply described in the source layer
I hope this sharing can be of some help to your work.