🐛 keep the lifecycle execution order while multiple apps mounting on the same container parallelly (#1625)
This commit is contained in:
parent
44ccad92de
commit
9e4274e3f8
58
src/apis.ts
58
src/apis.ts
|
|
@ -52,6 +52,7 @@ export function registerMicroApps<T extends ObjectType>(
|
|||
}
|
||||
|
||||
const appConfigPromiseGetterMap = new Map<string, Promise<ParcelConfigObjectGetter>>();
|
||||
const containerMicroAppsMap = new Map<string, MicroApp[]>();
|
||||
|
||||
export function loadMicroApp<T extends ObjectType>(
|
||||
app: LoadableApp<T>,
|
||||
|
|
@ -69,9 +70,39 @@ export function loadMicroApp<T extends ObjectType>(
|
|||
return undefined;
|
||||
};
|
||||
|
||||
let microApp: MicroApp;
|
||||
const wrapParcelConfigForRemount = (config: ParcelConfigObject): ParcelConfigObject => {
|
||||
const container = 'container' in app ? app.container : undefined;
|
||||
|
||||
let microAppConfig = config;
|
||||
if (container) {
|
||||
const xpath = getContainerXpath(container);
|
||||
if (xpath) {
|
||||
const containerMicroApps = containerMicroAppsMap.get(`${name}-${xpath}`);
|
||||
if (containerMicroApps?.length) {
|
||||
const mount = [
|
||||
async () => {
|
||||
// While there are multiple micro apps mounted on the same container, we must wait until the prev instances all had unmounted
|
||||
// Otherwise it will lead some concurrent issues
|
||||
const prevLoadMicroApps = containerMicroApps.slice(0, containerMicroApps.indexOf(microApp));
|
||||
const prevLoadMicroAppsWhichNotBroken = prevLoadMicroApps.filter(
|
||||
(v) => v.getStatus() !== 'LOAD_ERROR' && v.getStatus() !== 'SKIP_BECAUSE_BROKEN',
|
||||
);
|
||||
await Promise.all(prevLoadMicroAppsWhichNotBroken.map((v) => v.unmountPromise));
|
||||
},
|
||||
...toArray(microAppConfig.mount),
|
||||
];
|
||||
|
||||
microAppConfig = {
|
||||
...config,
|
||||
mount,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
...config,
|
||||
...microAppConfig,
|
||||
// empty bootstrap hook which should not run twice while it calling from cached micro app
|
||||
bootstrap: () => Promise.resolve(),
|
||||
};
|
||||
|
|
@ -123,7 +154,30 @@ export function loadMicroApp<T extends ObjectType>(
|
|||
startSingleSpa({ urlRerouteOnly: frameworkConfiguration.urlRerouteOnly ?? defaultUrlRerouteOnly });
|
||||
}
|
||||
|
||||
return mountRootParcel(memorizedLoadingFn, { domElement: document.createElement('div'), ...props });
|
||||
microApp = mountRootParcel(memorizedLoadingFn, { domElement: document.createElement('div'), ...props });
|
||||
|
||||
// Store the microApps which they mounted on the same container
|
||||
const container = 'container' in app ? app.container : undefined;
|
||||
if (container) {
|
||||
const xpath = getContainerXpath(container);
|
||||
if (xpath) {
|
||||
const key = `${name}-${xpath}`;
|
||||
|
||||
const microAppsRef = containerMicroAppsMap.get(key) || [];
|
||||
microAppsRef.push(microApp);
|
||||
containerMicroAppsMap.set(key, microAppsRef);
|
||||
|
||||
// gc after unmount
|
||||
microApp.unmountPromise.finally(() => {
|
||||
const index = microAppsRef.indexOf(microApp);
|
||||
microAppsRef.splice(index, 1);
|
||||
// @ts-ignore
|
||||
microApp = null;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return microApp;
|
||||
}
|
||||
|
||||
export function start(opts: FrameworkConfiguration = {}) {
|
||||
|
|
|
|||
|
|
@ -341,15 +341,8 @@ export async function loadApp<T extends ObjectType>(
|
|||
const syncAppWrapperElement2Sandbox = (element: HTMLElement | null) => (initialAppWrapperElement = element);
|
||||
|
||||
const parcelConfigGetter: ParcelConfigObjectGetter = (remountContainer = initialContainer) => {
|
||||
let appWrapperElement: HTMLElement | null = initialAppWrapperElement;
|
||||
const appWrapperGetter = getAppWrapperGetter(
|
||||
appName,
|
||||
appInstanceId,
|
||||
!!legacyRender,
|
||||
strictStyleIsolation,
|
||||
scopedCSS,
|
||||
() => appWrapperElement,
|
||||
);
|
||||
let appWrapperElement: HTMLElement | null;
|
||||
let appWrapperGetter: ReturnType<typeof getAppWrapperGetter>;
|
||||
|
||||
const parcelConfig: ParcelConfigObject = {
|
||||
name: appInstanceId,
|
||||
|
|
@ -371,6 +364,18 @@ export async function loadApp<T extends ObjectType>(
|
|||
|
||||
return undefined;
|
||||
},
|
||||
// initial wrapper element before app mount/remount
|
||||
async () => {
|
||||
appWrapperElement = initialAppWrapperElement;
|
||||
appWrapperGetter = getAppWrapperGetter(
|
||||
appName,
|
||||
appInstanceId,
|
||||
!!legacyRender,
|
||||
strictStyleIsolation,
|
||||
scopedCSS,
|
||||
() => appWrapperElement,
|
||||
);
|
||||
},
|
||||
// 添加 mount hook, 确保每次应用加载前容器 dom 结构已经设置完毕
|
||||
async () => {
|
||||
const useNewContainer = remountContainer !== initialContainer;
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user