diff --git a/src/create_app.ts b/src/create_app.ts index 3803d47c..4e0e4607 100644 --- a/src/create_app.ts +++ b/src/create_app.ts @@ -78,6 +78,7 @@ export default class CreateApp implements AppInterface { public isPrefetch: boolean public isPrerender: boolean public prefetchLevel?: number + public mountIdentifier?: symbol public fiber = false public routerMode: string public attrs?: Record @@ -287,6 +288,7 @@ export default class CreateApp implements AppInterface { this.routerMode = routerMode const dispatchBeforeMount = () => { + this.mountIdentifier = Symbol('mountIdentifier') dispatchLifecyclesEvent( this.container, this.name, @@ -493,6 +495,8 @@ export default class CreateApp implements AppInterface { unmountcb?: CallableFunction, umdHookUnmountResult?: unknown, ): void { + this.mountIdentifier = undefined + // dispatch state event to micro app dispatchCustomEventToMicroApp(this, 'statechange', { appState: appStates.UNMOUNT diff --git a/src/source/scripts.ts b/src/source/scripts.ts index 5fe38efd..3f53271b 100644 --- a/src/source/scripts.ts +++ b/src/source/scripts.ts @@ -320,11 +320,21 @@ export function fetchScriptsFromHtml ( const fiberScriptTasks: fiberTasks = app.isPrefetch || app.fiber ? [] : null + const startIdentifier = app.mountIdentifier + if (fetchScriptPromise.length) { promiseStream(fetchScriptPromise, (res: {data: string, index: number}) => { + const address = fetchScriptPromiseInfo[res.index][0] + const scriptInfo = fetchScriptPromiseInfo[res.index][1] + const currIdentifier = app.mountIdentifier + if (startIdentifier !== currIdentifier) { + scriptInfo.code = res.data + // app has been unmounted, old sandbox is not valid anymore. should not execute script + throw new Error('app has been unmounted, abort loading script') + } injectFiberTask(fiberScriptTasks, () => fetchScriptSuccess( - fetchScriptPromiseInfo[res.index][0], - fetchScriptPromiseInfo[res.index][1], + address, + scriptInfo, res.data, app, )) @@ -578,8 +588,14 @@ export function runDynamicRemoteScript ( if (scriptInfo.code || isTypeModule(app, scriptInfo)) { defer(runDynamicScript) } else { + const startIdentifier = app.mountIdentifier fetchSource(address, app.name).then((code: string) => { + const currIdentifier = app.mountIdentifier scriptInfo.code = code + if (startIdentifier !== currIdentifier) { + // app has been unmounted, old sandbox is not valid anymore. should not execute script + throw new Error('app has been unmounted, abort loading script') + } runDynamicScript() }).catch((err) => { logError(err, app.name) diff --git a/typings/global.d.ts b/typings/global.d.ts index 18d4132f..6386b1f6 100644 --- a/typings/global.d.ts +++ b/typings/global.d.ts @@ -190,6 +190,7 @@ declare module '@micro-app/types' { isPrerender: boolean isReloading?: boolean prefetchLevel?: number + mountIdentifier?: symbol // used to identify if the app has been unmounted between async calls // defaultPage: string // default page when mount // baseroute: string // route prefix, default is '' // hiddenRouter: boolean // hide router info of child from browser url