introduction
Note: Due to space limitations, the codes pasted in this article have been deleted and sorted out by the author, and are only displayed to tell the principles of the qiankun framework, not the complete source code.
1. Introduction to single-spa
To understand the implementation mechanism of qiankun, we have to start with the single-spa it depends on. With the development of micro front-end, we have seen a variety of toolkits and frameworks appear in this field to help us realize our own micro front-end applications conveniently and quickly. In the early stages of development, single-spa can be said to be unique, providing us with a simple micro front-end routing tool, which greatly reduces the cost of implementing a micro front-end application. Let’s take a look at the architecture and code of a typical single-spa micro front-end application.
Main application (base):
As the project scheduling center in the entire micro front-end application, it is the first part that the user loads when entering the micro front-end application. In the main application, provided to single-sparegisterApplication
The function passes in the specified parameters to register the sub-application, including the sub-application namename
, How to load sub-appsapp
, When will the sub-app be activatedactiveWhen
and parameters to be passed to sub-applicationscustomProps
etc. Called after completing the overall registrationstart
The function starts the entire micro front-end project.
// import { registerApplication, start } from 'single-spa'; // Config with more expressive API registerApplication({ name: 'app1', app: () => import('src/app1/'), activeWhen: ['/myApp', (location) => ('/some/other/path')], customProps: { some: 'value', } }); start();
Sub-application:
The sub-application is the part that actually displays the content. The most important task is to export the life cycle functions specified in single-spa to facilitate the main application scheduling. Among them, bootstrap is called when the sub-app is loaded for the first time, mount is called when the sub-app is activated, and unmount is called when the sub-app is moved out. In addition, in these life cycle functions, we can see that the props parameter is passed in. This parameter contains information such as the sub-application registration name, singleSpa instance, user-defined parameters, etc., which is convenient for the use of sub-applications.
("The registered application has been loaded!"); export async function bootstrap(props) { const { name, // The name of the application singleSpa, // The singleSpa instance mountParcel, // Function for manually mounting customProps, // Additional custom information } = props; // Props are given to every lifecycle return (); } export async function mount(props) {...} export async function unmount(props) {...}
We can see that Single-spa is the most widely used package in the field of micro front-end frameworks, which provides us with a good sub-application routing mechanism. But in addition to this, single-spa also leaves many problems that need to be solved by users:
How should sub-apps be loaded and where should they be loaded?
Will sub-app runtime affect each other?
The main application, the sub-application and the sub-application can communicate with each other through customProps, but how can we know that the customProps has changed?
Therefore, many micro front-end frameworks based on single-spa secondary packaging have appeared on the market. They used different methods and packaged more complete products based on their different focuses. For these products, we can understand the role of single-spa in it as the role of react-router in react projects. single-spa, as a micro front-end routing without framework and technology stack limitation, provides them with the lowest-level inter-sub-application routing and life cycle management services. In the development and growth of micro front-end in recent years, the Alibaba qiankun framework, which was launched in the early stage and enduring, is considered a unique success.
2. Brief introduction of qiankun
As the leading framework in the micro front-end field, qiankun is remarkable in terms of the convenience of access and the ease of use provided by the framework itself. Qiankun has carried out secondary development based on single-spa. It not only provides users with simple access methods (including reducing intrusion and easy transformation of old projects), but also considerately provides sandbox isolation and implements inter-application communication methods based on publish and subscription mode, which greatly reduces the entry threshold for micro front-ends, and the promotion of micro front-end engineering cannot be ignored.
Because it is based on single-spa secondary development, the qiankun micro front-end architecture is no different from what is mentioned in Chapter 1. Below we list the code of a typical qiankun application and compare it with the code of single-spa.
Main application:
Here qiankun changed the app in single-spa to entry and enhanced its functions. Users only need to enter the html entry path of the sub-app. The rest of the loading work is completed internally by qiankun. Of course, you can also list the resources you need to load by yourself. In addition, the container option is added, allowing users to display and perceive the container mounted by the sub-app, simplifying the scenario of multiple sub-app activation at the same time.
import { registerMicroApps, start } from 'qiankun'; registerMicroApps([ { name: 'react app', // app name registered entry: '//localhost:7100', container: '#yourContainer', activeRule: '/yourActiveRule', }, { name: 'vue app', entry: { scripts: ['//localhost:7100/'] }, container: '#yourContainer2', activeRule: '/yourActiveRule2', }, ]); start();
Sub-application:
Basically consistent with single-spa, three life cycle functions are derived. Here we can see that in mount we manually render the react application to the page, and in unmount we clear it from the page.
/** * bootstrap will only be called once when the micro-application is initialized. The next time the micro-application re-enters, the mount hook will be called directly, and bootstrap will not be triggered repeatedly. * Usually we can initialize some global variables here, such as application-level cache that will not be destroyed in the unmount stage, etc. */ export async function bootstrap() { ('react app bootstraped'); } /** * The mount method is called every time the application enters, and we usually trigger the application's rendering method here. */ export async function mount(props) { (<App />, ? ('#root') : ('root')); } /** * The method that will be called every time the application is cut out/uninstalled. Usually, we will uninstall the application instance of the micro application. */ export async function unmount(props) { ( ? ('#root') : ('root'), ); }
As you can see, since it helps us complete the loading of sub-apps, the user's configuration is easier than single-spa. However, in addition to this open work, qiankun has also made a lot of efforts for our ease of use in the dark. Next, we will conduct in-depth analysis of qiankun's internal source code and related implementation principles around the following three aspects:
How to implement qiankun: Users can load the corresponding sub-application resources by just configuring a URL;
How qiankun helps users to run independently between sub-applications (including JS and CSS without affecting each other);
How qiankun helps users achieve simpler and more efficient inter-application communication;
3. Sub-app loading
The method of registering sub-applications of qiankun is very simple. Users only need to call the registerMicroApps function and pass the required parameters in. In the previous article, we mentioned that qiankun is a framework based on single-spa secondary encapsulation, so the routing listening and sub-application life cycle management in qiankun are actually left to single-spa for implementation. Let's take a look at how this method is implemented (partially intercepted)
import { registerApplication } from 'single-spa'; let microApps: Array<RegistrableApp<Record<string, unknown>>> = []; export function registerMicroApps<T extends ObjectType>( apps: Array<RegistrableApp<T>>, lifeCycles?: FrameworkLifeCycles<T>, ) { // Determine whether the application has been registered and ensure that each application is registered only once const unregisteredApps = ((app) => !((registeredApp) => === )); microApps = [...microApps, ...unregisteredApps]; ((app) => { // Take out the parameters entered by the user const { name, activeRule, loader = noop, props, ...appConfig } = app; // Call the single-spa sub-app registration function to convert the parameters entered by the user into the parameters required by the single-spa registerApplication({ name, // Here is a function that requires the sub-application loading method for single-spa app: async () => { loader(true); await ; // Call the conversion function loadApp to parse the url input by the user, and finally generate the enhanced sub-application lifecycle functions (including mount, unmount, bootstrap) const { mount, ...otherMicroAppConfigs } = ( await loadApp({ name, props, ...appConfig }, frameworkConfiguration, lifeCycles) )(); // The return value is a series of life cycle functions generated by loadApp, and the mount function array is enhanced again return { mount: [async () => loader(true), ...toArray(mount), async () => loader(false)], ...otherMicroAppConfigs, }; }, activeWhen: activeRule, customProps: props, }); }); }
As you can see, what qiankun does on the loading of sub-app is to call the userregisterMicroApps
The parameters provided during the period are converted into a single-sparegisterApplication
Required parameters. Below, we give the main function in qiankun to implement this conversion subloadApp
Partial implementation code (source code address/umijs/qiank…
import { importEntry } from 'import-html-entry'; export async function loadApp<T extends ObjectType>( app: LoadableApp<T>, configuration: FrameworkConfiguration = {}, lifeCycles?: FrameworkLifeCycles<T>, ): Promise<ParcelConfigObjectGetter> { const { entry, name: appName } = app; const { singular = false, sandbox = true, excludeAssetFilter, globalContext = window, ...importEntryOpts } = configuration; // 。。。。。。 // Relying on the method in the import-html-entry library, the url (entry parameter) entered by the user is parsed, and template (HTML template), execScripts (execution function of the JS file that depends on) and assetPublicPath (public resource path) const { template, execScripts, assetPublicPath } = await importEntry(entry, importEntryOpts); // 。。。。。。 // Execute entry-dependent js file in window sandbox (global parameters) to get the relevant life cycle (bootstrap, mount, unmount, update) // Here you can ignore the getLifecyclesFromExports function, which returns are consistent with scriptExports, just to check whether the sub-app has exported the necessary life cycle const scriptExports: any = await execScripts(global, sandbox && !useLooseSandbox, { scopedGlobalVariables: speedySandbox ? trustedGlobals : [], }); const { bootstrap, mount, unmount, update } = getLifecyclesFromExports( scriptExports, appName, global, sandboxContainer?.instance?.latestSetProp, ); // 。。。。。 // Export the getter method required to configure single-spa (because the configuration item is related to the container on which the sub-app is hung, the default is the container entered by the user. Sub-app can manually load the sub-app and specify its rendering location) const initialContainer = 'container' in app ? : undefined; const parcelConfigGetter: ParcelConfigObjectGetter = (remountContainer = initialContainer) => { const parcelConfig: ParcelConfigObject = { name: appInstanceId, bootstrap, // The mount array is executed in sequence when the sub-app renders mount: [ // 。。。。。。 // Perform sandbox isolation mountSandbox, // Call the user-defined mount life cycle and pass in the inter-application communication method function of setGlobalState/onGlobalStateChange async (props) => mount({ ...props, container: appWrapperGetter(), setGlobalState, onGlobalStateChange }), // 。。。。。。 ], // The unmount array is executed in sequence when the sub-app is unloaded unmount: [ // 。。。。。。。 // Call user-defined unmount lifecycle async (props) => unmount({ ...props, container: appWrapperGetter() }), // Uninstall the quarantine sandbox unmountSandbox, // Cleaning up async () => { render({ element: null, loading: false, container: remountContainer }, 'unmounted'); // Clean up sub-app subscriptions to global communications offGlobalStateChange(appInstanceId); // for gc appWrapperElement = null; syncAppWrapperElement2Sandbox(appWrapperElement); }, // 。。。。。。。 ], }; return parcelConfig; } return parcelConfigGetter }
You can see that qiankun loads the function in itloadApp
Some extra work was done in the
For ease of use, qiankun provides a way to load sub-applications based on the url entry. In order to obtain the html file (or resource file array) provided by the user and parse the required resources in it, qiankun relies on itimport-html-entry
The relevant methods in the library are executed and the user-defined life cycle exported by the sub-app.
Enhance the user-defined life cycle (including mount/uninstall the isolation sandbox between applications, initialization or incoming the communication method between applications, etc.), return the framework enhanced life cycle function array and register it in single-spa.
Through source code analysis, we can see that qiankun exists as an intermediate layer in sub-application loading. Its main function is to simplify the user's input for sub-application registration, convert and enhance user's input through internal methods of the framework, and finally pass it into single-spa. In subsequent execution, the single-spa is the real responsible for sub-application loading and unloading.
The following article of analysis of the source code of the micro front-end framework qiankun
The above is the detailed content of the above article of the analysis of the source code of the micro front-end framework qiankun. For more information about the analysis of the micro front-end framework qiankun, please pay attention to my other related articles!