- Micro front End 01: Js isolation mechanism analysis (Snapshot sandbox, two kinds of proxy sandbox)
- Micro Front End 02: Analysis of microapplication loading process (from registration of microapplication to internal implementation of loadApp method)
- Micro front End 03: Sandbox Container Analysis of Universe (concrete application of Js sandbox mechanism after establishment)
- Microfront 04: Resource loading mechanism for The Universe (internal implementation of import-HTml-Entry)
- Micro front End 05: Implementation of loadMicroApp method and analysis of data communication mechanism
In the previous article, we analyzed the process of registering microapplications, analyzed the process of loading microapplications, and went deep into import-HTML-entry to understand the specific mechanism of obtaining microapplication resources. In front of the work, we can support after the change in the routing automatically load and mount the different application, in other words, our program the best at some point in time can mount a small application, because this kind of micro application loading and mount of the automatic before and after the application is registered, we can’t intervene too much. In fact, Qiankun also supports the same page, loading multiple applications. So how do you implement this mechanism? Which brings us to today’s topic, manual loading of microapps, which means we can load any microapp at any point in time and mount it anywhere, and since we’re so flexible, we can of course display multiple microapps in front of the user at the same time. The ability to manually load microapplications is provided by the loadMicroApp function, which this article walks you through.
Main logic of loadMicroApp
// Snippet 1
export function loadMicroApp<T extends ObjectType> (app: LoadableApp
, configuration? : FrameworkConfiguration & { autoStart? : boolean }, lifeCycles? : FrameworkLifeCycles
,
) :MicroApp {
// A lot of code is omitted here...
const memorizedLoadingFn = async() :Promise<ParcelConfigObject> => {
// A lot of code is omitted here...
const parcelConfigObjectGetterPromise = loadApp(app, userConfiguration, lifeCycles);
// A lot of code is omitted here...
return (await parcelConfigObjectGetterPromise)(container);
};
// A lot of code is omitted here...
microApp = mountRootParcel(memorizedLoadingFn, { domElement: document.createElement('div'), ...props });
// A lot of code is omitted here...
return microApp;
}
Copy the code
I’ve omitted a lot of secondary logic for ease of understanding, so let’s look at what loadMicroApp basically does. The end result is that the mountRootParcel function is called and its return value is the return value of the loadMicroApp function. What is mountRootParcel? The mountRootParcel function is imported from single-SPA and its core function is to return an object:
externalRepresentation = {
mount() {
// omit a lot of code...
},
unmount() {
// omit a lot of code...
},
getStatus() {
// omit a lot of code...
},
loadPromise: promiseWithoutReturnValue(loadPromise),
bootstrapPromise: promiseWithoutReturnValue(bootstrapPromise),
mountPromise: promiseWithoutReturnValue(mountPromise),
unmountPromise: promiseWithoutReturnValue(unmountPromise),
};
Copy the code
After returning the object, we can operate the methods of the object, mount, unload, obtain the microapplication state, and execute the life cycle method of the microapplication. I’ll dive into the main logic and details of the entire library in a later article on Single-SPA, but for now we just need to know that calling the mountRootParcel function returns an object that can manipulate the microapplication. Let’s start with the function memorizedLoadingFn in snippet 1 of this article. The core logic of this function is to call the microapplication loading function loadApp and return an object containing the lifecycle methods exposed by the microapplication. That is, mountRootParcel internally calls the memorizedLoadingFn to retrieve the relevant lifecycle methods exposed by the microapplication. The loadApp function returns an object that has a life cycle function for the microapplication, which controls the behavior of the microapplication. What is the point of loadApp calling a single-SPA mountRootParcel function after it has executed, when it returns an object that can contain control over microapplication behavior? Actually exposed life cycle method, function is relatively weak, such as mount and unmount to generally is simply the DOM node associated mounted to a certain place or uninstall from this place, but can control their rendering or not, but what the micro applications where the rendering time rendering is a bit difficult, Because microapplications may work under any possible parent application. Single-spa, as a basic framework, acts as a controller that functions such as load, bootstrap, mount, unmount, and so on must be executed sequtively. This controller manages the various states of the micro-application and other capabilities of the micro-application. I will expand on how single-SPA works in a later article. That’s enough for this article, but knowing that the return value of the mountRootParcel function allows you to control the corresponding microapplication.
In fact, here, combined with the previous articles, we can say that we have a more in-depth understanding of the universe, the main API corresponding to the implementation and principle is also more clear. In this paper, we will discuss some points worth understanding in the universe, and the analysis of the universe will come to an end for the time being. The rest of the time is mainly devoted to the analysis of single-SPA. After analyzing single-SPA, we will go back and analyze the whole structure of Qiankun to gain insight into the design ideas. Please look forward to friends.
Data communication mechanism
Data transfer for master and child applications
When registering a micro-application, there is an optional parameter: props
export function registerMicroApps<T extends ObjectType> (
apps: Array<RegistrableApp<T>>, lifeCycles? : FrameworkLifeCycles<T>,) {
// omit a lot of code...
unregisteredApps.forEach((app) = > {
const{ name, activeRule, loader = noop, props, ... appConfig } = app; registerApplication({ name,app: async() = > {// omit a lot of code...
const{ mount, ... otherMicroAppConfigs } = (awaitloadApp({ name, props, ... appConfig }, frameworkConfiguration, lifeCycles) )();// omit a lot of code...
},
activeWhen: activeRule,
customProps: props,
});
});
}
Copy the code
As you can see from the code, the props parameters are passed in when the micro-application is loaded. In fact, these parameters can be obtained when the micro-application executes the lifecycle method. This makes it the simplest thing between parent and child applications.
Global event communication
The parent application can pass parameters to the child application. What if it passes a function? That’s right, the normal communication mechanism has led to a much more powerful communication mechanism, communication through global events. The functions mentioned above mainly refer to the following two functions (actually there are more than two, but they are relatively important) :
// Note: For ease of understanding, there are some discrepancies between the following code and the source code...
function onGlobalStateChange(callback: OnGlobalStateChangeCallback, fireImmediately? : boolean) {
// This function listens for events and saves the incoming callback function
};
function setGlobalState(state: Record<string, any> = {}) {
// This function is used to update data and trigger global events to call the corresponding callback saved by onGlobalStateChange
}
Copy the code
The setGlobalState function mentions raising global events. To do so, look at the following code:
function emitGlobal(state: Record<string, any>, prevState: Record<string, any>) {
Object.keys(deps).forEach((id: string) = > {
if (deps[id] instanceof Function) { deps[id](cloneDeep(state), cloneDeep(prevState)); }}); }Copy the code
The deps[id] in the above code corresponds to the callback saved in onGlobalStateChange. Recently, due to work pressure and limited time in the evening, THERE is no time to present more details in the article. Although the cache mechanism mentioned above or the code appeared, deep cloning and so on, although basic, but for many friends who do not have a solid foundation, it is actually very important and necessary to mention. I hope there will be a book or video in the future that presents more details about great open source frameworks. That’s all for this article, so please look forward to my next series of articles on single-SPA analysis.
Welcome to follow my wechat official account: Yang Yitao, you can get the latest news.
After reading this article, feel the harvest of friends like it, can improve the digging force value, I hope to become an excellent writer of digging gold this year.