use new unique instance name generator (#1859)

This commit is contained in:
Kuitos 2021-12-08 20:36:42 +08:00 committed by GitHub
parent 7e380f823e
commit b8571b2346
3 changed files with 48 additions and 27 deletions

View File

@ -23,7 +23,7 @@ test('should wrap the id [2]', () => {
test('should wrap string with div', () => { test('should wrap string with div', () => {
const tpl = '<span>qiankun</span>'; const tpl = '<span>qiankun</span>';
const factory = getDefaultTplWrapper('react16', 'react16'); const factory = getDefaultTplWrapper('react16');
const ret = factory(tpl); const ret = factory(tpl);

View File

@ -20,13 +20,14 @@ import type {
import { createSandboxContainer, css } from './sandbox'; import { createSandboxContainer, css } from './sandbox';
import { import {
Deferred, Deferred,
getAppInstanceName,
getContainer, getContainer,
getDefaultTplWrapper, getDefaultTplWrapper,
getWrapperId, getWrapperId,
isEnableScopedCSS, isEnableScopedCSS,
performanceGetEntriesByName,
performanceMark, performanceMark,
performanceMeasure, performanceMeasure,
performanceGetEntriesByName,
toArray, toArray,
validateExportLifecycle, validateExportLifecycle,
} from './utils'; } from './utils';
@ -111,7 +112,6 @@ function createElement(
/** generate app wrapper dom getter */ /** generate app wrapper dom getter */
function getAppWrapperGetter( function getAppWrapperGetter(
appName: string, appName: string,
appInstanceId: string,
useLegacyRender: boolean, useLegacyRender: boolean,
strictStyleIsolation: boolean, strictStyleIsolation: boolean,
scopedCSS: boolean, scopedCSS: boolean,
@ -122,13 +122,13 @@ function getAppWrapperGetter(
if (strictStyleIsolation) throw new QiankunError('strictStyleIsolation can not be used with legacy render!'); if (strictStyleIsolation) throw new QiankunError('strictStyleIsolation can not be used with legacy render!');
if (scopedCSS) throw new QiankunError('experimentalStyleIsolation can not be used with legacy render!'); if (scopedCSS) throw new QiankunError('experimentalStyleIsolation can not be used with legacy render!');
const appWrapper = document.getElementById(getWrapperId(appInstanceId)); const appWrapper = document.getElementById(getWrapperId(appName));
assertElementExist(appWrapper, `Wrapper element for ${appName} with instance ${appInstanceId} is not existed!`); assertElementExist(appWrapper, `Wrapper element for ${appName} is not existed!`);
return appWrapper!; return appWrapper!;
} }
const element = elementGetter(); const element = elementGetter();
assertElementExist(element, `Wrapper element for ${appName} with instance ${appInstanceId} is not existed!`); assertElementExist(element, `Wrapper element for ${appName} is not existed!`);
if (strictStyleIsolation && supportShadowDOM) { if (strictStyleIsolation && supportShadowDOM) {
return element!.shadowRoot!; return element!.shadowRoot!;
@ -246,10 +246,10 @@ export async function loadApp<T extends ObjectType>(
configuration: FrameworkConfiguration = {}, configuration: FrameworkConfiguration = {},
lifeCycles?: FrameworkLifeCycles<T>, lifeCycles?: FrameworkLifeCycles<T>,
): Promise<ParcelConfigObjectGetter> { ): Promise<ParcelConfigObjectGetter> {
const { entry, name: appName } = app; const { entry } = app;
const appInstanceId = `${appName}_${+new Date()}_${Math.floor(Math.random() * 1000)}`; const appInstanceName = getAppInstanceName(app.name);
const markName = `[qiankun] App ${appInstanceId} Loading`; const markName = `[qiankun] App ${appInstanceName} Loading`;
if (process.env.NODE_ENV === 'development') { if (process.env.NODE_ENV === 'development') {
performanceMark(markName); performanceMark(markName);
} }
@ -272,7 +272,7 @@ export async function loadApp<T extends ObjectType>(
await (prevAppUnmountedDeferred && prevAppUnmountedDeferred.promise); await (prevAppUnmountedDeferred && prevAppUnmountedDeferred.promise);
} }
const appContent = getDefaultTplWrapper(appInstanceId, appName)(template); const appContent = getDefaultTplWrapper(appInstanceName)(template);
const strictStyleIsolation = typeof sandbox === 'object' && !!sandbox.strictStyleIsolation; const strictStyleIsolation = typeof sandbox === 'object' && !!sandbox.strictStyleIsolation;
@ -287,21 +287,20 @@ export async function loadApp<T extends ObjectType>(
appContent, appContent,
strictStyleIsolation, strictStyleIsolation,
scopedCSS, scopedCSS,
appName, appInstanceName,
); );
const initialContainer = 'container' in app ? app.container : undefined; const initialContainer = 'container' in app ? app.container : undefined;
const legacyRender = 'render' in app ? app.render : undefined; const legacyRender = 'render' in app ? app.render : undefined;
const render = getRender(appName, appContent, legacyRender); const render = getRender(appInstanceName, appContent, legacyRender);
// 第一次加载设置应用可见区域 dom 结构 // 第一次加载设置应用可见区域 dom 结构
// 确保每次应用加载前容器 dom 结构已经设置完毕 // 确保每次应用加载前容器 dom 结构已经设置完毕
render({ element: initialAppWrapperElement, loading: true, container: initialContainer }, 'loading'); render({ element: initialAppWrapperElement, loading: true, container: initialContainer }, 'loading');
const initialAppWrapperGetter = getAppWrapperGetter( const initialAppWrapperGetter = getAppWrapperGetter(
appName, appInstanceName,
appInstanceId,
!!legacyRender, !!legacyRender,
strictStyleIsolation, strictStyleIsolation,
scopedCSS, scopedCSS,
@ -315,7 +314,7 @@ export async function loadApp<T extends ObjectType>(
let sandboxContainer; let sandboxContainer;
if (sandbox) { if (sandbox) {
sandboxContainer = createSandboxContainer( sandboxContainer = createSandboxContainer(
appName, appInstanceName,
// FIXME should use a strict sandbox logic while remount, see https://github.com/umijs/qiankun/issues/518 // FIXME should use a strict sandbox logic while remount, see https://github.com/umijs/qiankun/issues/518
initialAppWrapperGetter, initialAppWrapperGetter,
scopedCSS, scopedCSS,
@ -343,13 +342,13 @@ export async function loadApp<T extends ObjectType>(
const scriptExports: any = await execScripts(global, sandbox && !useLooseSandbox); const scriptExports: any = await execScripts(global, sandbox && !useLooseSandbox);
const { bootstrap, mount, unmount, update } = getLifecyclesFromExports( const { bootstrap, mount, unmount, update } = getLifecyclesFromExports(
scriptExports, scriptExports,
appName, appInstanceName,
global, global,
sandboxContainer?.instance?.latestSetProp, sandboxContainer?.instance?.latestSetProp,
); );
const { onGlobalStateChange, setGlobalState, offGlobalStateChange }: Record<string, CallableFunction> = const { onGlobalStateChange, setGlobalState, offGlobalStateChange }: Record<string, CallableFunction> =
getMicroAppStateActions(appInstanceId); getMicroAppStateActions(appInstanceName);
// FIXME temporary way // FIXME temporary way
const syncAppWrapperElement2Sandbox = (element: HTMLElement | null) => (initialAppWrapperElement = element); const syncAppWrapperElement2Sandbox = (element: HTMLElement | null) => (initialAppWrapperElement = element);
@ -359,7 +358,7 @@ export async function loadApp<T extends ObjectType>(
let appWrapperGetter: ReturnType<typeof getAppWrapperGetter>; let appWrapperGetter: ReturnType<typeof getAppWrapperGetter>;
const parcelConfig: ParcelConfigObject = { const parcelConfig: ParcelConfigObject = {
name: appInstanceId, name: appInstanceName,
bootstrap, bootstrap,
mount: [ mount: [
async () => { async () => {
@ -382,8 +381,7 @@ export async function loadApp<T extends ObjectType>(
async () => { async () => {
appWrapperElement = initialAppWrapperElement; appWrapperElement = initialAppWrapperElement;
appWrapperGetter = getAppWrapperGetter( appWrapperGetter = getAppWrapperGetter(
appName, appInstanceName,
appInstanceId,
!!legacyRender, !!legacyRender,
strictStyleIsolation, strictStyleIsolation,
scopedCSS, scopedCSS,
@ -396,7 +394,7 @@ export async function loadApp<T extends ObjectType>(
if (useNewContainer || !appWrapperElement) { if (useNewContainer || !appWrapperElement) {
// element will be destroyed after unmounted, we need to recreate it if it not exist // element will be destroyed after unmounted, we need to recreate it if it not exist
// or we try to remount into a new container // or we try to remount into a new container
appWrapperElement = createElement(appContent, strictStyleIsolation, scopedCSS, appName); appWrapperElement = createElement(appContent, strictStyleIsolation, scopedCSS, appInstanceName);
syncAppWrapperElement2Sandbox(appWrapperElement); syncAppWrapperElement2Sandbox(appWrapperElement);
} }
@ -417,7 +415,7 @@ export async function loadApp<T extends ObjectType>(
}, },
async () => { async () => {
if (process.env.NODE_ENV === 'development') { if (process.env.NODE_ENV === 'development') {
const measureName = `[qiankun] App ${appInstanceId} Loading Consuming`; const measureName = `[qiankun] App ${appInstanceName} Loading Consuming`;
performanceMeasure(measureName, markName); performanceMeasure(measureName, markName);
} }
}, },
@ -429,7 +427,7 @@ export async function loadApp<T extends ObjectType>(
async () => execHooksChain(toArray(afterUnmount), app, global), async () => execHooksChain(toArray(afterUnmount), app, global),
async () => { async () => {
render({ element: null, loading: false, container: remountContainer }, 'unmounted'); render({ element: null, loading: false, container: remountContainer }, 'unmounted');
offGlobalStateChange(appInstanceId); offGlobalStateChange(appInstanceName);
// for gc // for gc
appWrapperElement = null; appWrapperElement = null;
syncAppWrapperElement2Sandbox(appWrapperElement); syncAppWrapperElement2Sandbox(appWrapperElement);

View File

@ -101,16 +101,39 @@ export function isBoundedFunction(fn: CallableFunction) {
return bounded; return bounded;
} }
export function getDefaultTplWrapper(id: string, name: string) { export function getDefaultTplWrapper(name: string) {
return (tpl: string) => `<div id="${getWrapperId(id)}" data-name="${name}" data-version="${version}">${tpl}</div>`; return (tpl: string) => `<div id="${getWrapperId(name)}" data-name="${name}" data-version="${version}">${tpl}</div>`;
} }
export function getWrapperId(id: string) { export function getWrapperId(name: string) {
return `__qiankun_microapp_wrapper_for_${snakeCase(id)}__`; return `__qiankun_microapp_wrapper_for_${snakeCase(name)}__`;
} }
export const nativeGlobal = new Function('return this')(); export const nativeGlobal = new Function('return this')();
/**
* get app instance name with the auto-increment approach
* @param appName
*/
export const getAppInstanceName = (appName: string): string => {
if (typeof nativeGlobal.__app_instance_name_map__?.[appName] === undefined) {
const prevAppsCount = nativeGlobal.__app_instance_name_map__ || {};
Object.defineProperty(nativeGlobal, '__app_instance_name_map__', {
enumerable: false,
writable: true,
value: {
...prevAppsCount,
[appName]: 0,
},
});
return appName;
}
nativeGlobal.__app_instance_name_map__[appName]++;
return `${appName}_${nativeGlobal.__app_instance_name_map__[appName]}`;
};
/** 校验子应用导出的 生命周期 对象是否正确 */ /** 校验子应用导出的 生命周期 对象是否正确 */
export function validateExportLifecycle(exports: any) { export function validateExportLifecycle(exports: any) {
const { bootstrap, mount, unmount } = exports ?? {}; const { bootstrap, mount, unmount } = exports ?? {};