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