🐛 compute container xpath at beginning to keep it consist around app running (#1725)

This commit is contained in:
Kuitos 2021-09-16 16:15:33 +08:00 committed by GitHub
parent c4b604228a
commit cbad8c8d7d
2 changed files with 36 additions and 34 deletions

View File

@ -1,12 +1,18 @@
import { noop } from 'lodash'; import { noop } from 'lodash';
import type { ParcelConfigObject } from 'single-spa'; import type { ParcelConfigObject } from 'single-spa';
import { mountRootParcel, registerApplication, start as startSingleSpa } from 'single-spa'; import { mountRootParcel, registerApplication, start as startSingleSpa } from 'single-spa';
import type { ObjectType } from './interfaces'; import type {
import type { FrameworkConfiguration, FrameworkLifeCycles, LoadableApp, MicroApp, RegistrableApp } from './interfaces'; FrameworkConfiguration,
FrameworkLifeCycles,
LoadableApp,
MicroApp,
ObjectType,
RegistrableApp,
} from './interfaces';
import type { ParcelConfigObjectGetter } from './loader'; import type { ParcelConfigObjectGetter } from './loader';
import { loadApp } from './loader'; import { loadApp } from './loader';
import { doPrefetchStrategy } from './prefetch'; import { doPrefetchStrategy } from './prefetch';
import { Deferred, getContainer, getXPathForElement, toArray } from './utils'; import { Deferred, getContainerXPath, toArray } from './utils';
let microApps: Array<RegistrableApp<Record<string, unknown>>> = []; let microApps: Array<RegistrableApp<Record<string, unknown>>> = [];
@ -80,24 +86,18 @@ export function loadMicroApp<T extends ObjectType>(
): MicroApp { ): MicroApp {
const { props, name } = app; const { props, name } = app;
const getContainerXpath = (container: string | HTMLElement): string | void => { const container = 'container' in app ? app.container : undefined;
const containerElement = getContainer(container); // Must compute the container xpath at beginning to keep it consist around app running
if (containerElement) { // If we compute it every time, the container dom structure most probably been changed and result in a different xpath value
return getXPathForElement(containerElement, document); const containerXPath = getContainerXPath(container);
} const appContainerXPathKey = `${name}-${containerXPath}`;
return undefined;
};
let microApp: MicroApp; let microApp: MicroApp;
const wrapParcelConfigForRemount = (config: ParcelConfigObject): ParcelConfigObject => { const wrapParcelConfigForRemount = (config: ParcelConfigObject): ParcelConfigObject => {
const container = 'container' in app ? app.container : undefined;
let microAppConfig = config; let microAppConfig = config;
if (container) { if (container) {
const xpath = getContainerXpath(container); if (containerXPath) {
if (xpath) { const containerMicroApps = containerMicroAppsMap.get(appContainerXPathKey);
const containerMicroApps = containerMicroAppsMap.get(`${name}-${xpath}`);
if (containerMicroApps?.length) { if (containerMicroApps?.length) {
const mount = [ const mount = [
async () => { async () => {
@ -137,7 +137,6 @@ export function loadMicroApp<T extends ObjectType>(
configuration ?? { ...frameworkConfiguration, singular: false }, configuration ?? { ...frameworkConfiguration, singular: false },
); );
const { $$cacheLifecycleByAppName } = userConfiguration; const { $$cacheLifecycleByAppName } = userConfiguration;
const container = 'container' in app ? app.container : undefined;
if (container) { if (container) {
// using appName as cache for internal experimental scenario // using appName as cache for internal experimental scenario
@ -146,9 +145,8 @@ export function loadMicroApp<T extends ObjectType>(
if (parcelConfigGetterPromise) return wrapParcelConfigForRemount((await parcelConfigGetterPromise)(container)); if (parcelConfigGetterPromise) return wrapParcelConfigForRemount((await parcelConfigGetterPromise)(container));
} }
const xpath = getContainerXpath(container); if (containerXPath) {
if (xpath) { const parcelConfigGetterPromise = appConfigPromiseGetterMap.get(appContainerXPathKey);
const parcelConfigGetterPromise = appConfigPromiseGetterMap.get(`${name}-${xpath}`);
if (parcelConfigGetterPromise) return wrapParcelConfigForRemount((await parcelConfigGetterPromise)(container)); if (parcelConfigGetterPromise) return wrapParcelConfigForRemount((await parcelConfigGetterPromise)(container));
} }
} }
@ -158,10 +156,7 @@ export function loadMicroApp<T extends ObjectType>(
if (container) { if (container) {
if ($$cacheLifecycleByAppName) { if ($$cacheLifecycleByAppName) {
appConfigPromiseGetterMap.set(name, parcelConfigObjectGetterPromise); appConfigPromiseGetterMap.set(name, parcelConfigObjectGetterPromise);
} else { } else if (containerXPath) appConfigPromiseGetterMap.set(appContainerXPathKey, parcelConfigObjectGetterPromise);
const xpath = getContainerXpath(container);
if (xpath) appConfigPromiseGetterMap.set(`${name}-${xpath}`, parcelConfigObjectGetterPromise);
}
} }
return (await parcelConfigObjectGetterPromise)(container); return (await parcelConfigObjectGetterPromise)(container);
@ -177,18 +172,14 @@ export function loadMicroApp<T extends ObjectType>(
microApp = 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) { if (container) {
const xpath = getContainerXpath(container); if (containerXPath) {
if (xpath) { // Store the microApps which they mounted on the same container
const key = `${name}-${xpath}`; const microAppsRef = containerMicroAppsMap.get(appContainerXPathKey) || [];
const microAppsRef = containerMicroAppsMap.get(key) || [];
microAppsRef.push(microApp); microAppsRef.push(microApp);
containerMicroAppsMap.set(key, microAppsRef); containerMicroAppsMap.set(appContainerXPathKey, microAppsRef);
const cleanApp = () => { const cleanup = () => {
const index = microAppsRef.indexOf(microApp); const index = microAppsRef.indexOf(microApp);
microAppsRef.splice(index, 1); microAppsRef.splice(index, 1);
// @ts-ignore // @ts-ignore
@ -196,7 +187,7 @@ export function loadMicroApp<T extends ObjectType>(
}; };
// gc after unmount // gc after unmount
microApp.unmountPromise.then(cleanApp).catch(cleanApp); microApp.unmountPromise.then(cleanup).catch(cleanup);
} }
} }

View File

@ -215,3 +215,14 @@ export function getXPathForElement(el: Node, document: Document): string | void
export function getContainer(container: string | HTMLElement): HTMLElement | null { export function getContainer(container: string | HTMLElement): HTMLElement | null {
return typeof container === 'string' ? document.querySelector(container) : container; return typeof container === 'string' ? document.querySelector(container) : container;
} }
export function getContainerXPath(container?: string | HTMLElement): string | void {
if (container) {
const containerElement = getContainer(container);
if (containerElement) {
return getXPathForElement(containerElement, document);
}
}
return undefined;
}