diff --git a/src/interfaces.ts b/src/interfaces.ts index 91c53ea..e424188 100644 --- a/src/interfaces.ts +++ b/src/interfaces.ts @@ -115,6 +115,8 @@ export interface SandBox { proxy: WindowProxy; /** 沙箱是否在运行中 */ sandboxRunning: boolean; + /** latest set property */ + latestSetProp?: PropertyKey | null; /** 启动沙箱 */ active(): void; /** 关闭沙箱 */ diff --git a/src/loader.ts b/src/loader.ts index 4a5cb7f..26121aa 100644 --- a/src/loader.ts +++ b/src/loader.ts @@ -9,7 +9,7 @@ import { LifeCycles, ParcelConfigObject } from 'single-spa'; import getAddOns from './addons'; import { getMicroAppStateActions } from './globalState'; import { FrameworkConfiguration, FrameworkLifeCycles, HTMLContentRender, LifeCycleFn, LoadableApp } from './interfaces'; -import { createSandbox, css } from './sandbox'; +import { createSandboxContainer, css } from './sandbox'; import { Deferred, getContainer, @@ -53,6 +53,7 @@ async function validateSingularMode( // @ts-ignore const supportShadowDOM = document.head.attachShadow || document.head.createShadowRoot; + function createElement( appContent: string, strictStyleIsolation: boolean, @@ -199,11 +200,24 @@ function getRender(appName: string, appContent: string, legacyRender?: HTMLConte return render; } -function getLifecyclesFromExports(scriptExports: LifeCycles, appName: string, global: WindowProxy) { +function getLifecyclesFromExports( + scriptExports: LifeCycles, + appName: string, + global: WindowProxy, + globalLatestSetProp?: PropertyKey | null, +) { if (validateExportLifecycle(scriptExports)) { return scriptExports; } + // fallback to sandbox latest set property if it had + if (globalLatestSetProp) { + const lifecycles = (global)[globalLatestSetProp]; + if (validateExportLifecycle(lifecycles)) { + return lifecycles; + } + } + if (process.env.NODE_ENV === 'development') { console.warn( `[qiankun] lifecycle not found from ${appName} entry exports, fallback to get from window['${appName}']`, @@ -223,6 +237,7 @@ function getLifecyclesFromExports(scriptExports: LifeCycles, appName: strin let prevAppUnmountedDeferred: Deferred; export type ParcelConfigObjectGetter = (remountContainer?: string | HTMLElement) => ParcelConfigObject; + export async function loadApp( app: LoadableApp, configuration: FrameworkConfiguration = {}, @@ -281,8 +296,9 @@ export async function loadApp( let mountSandbox = () => Promise.resolve(); let unmountSandbox = () => Promise.resolve(); const useLooseSandbox = typeof sandbox === 'object' && !!sandbox.loose; + let sandboxContainer; if (sandbox) { - const sandboxInstance = createSandbox( + sandboxContainer = createSandboxContainer( appName, // FIXME should use a strict sandbox logic while remount, see https://github.com/umijs/qiankun/issues/518 initialAppWrapperGetter, @@ -291,9 +307,9 @@ export async function loadApp( excludeAssetFilter, ); // 用沙箱的代理对象作为接下来使用的全局对象 - global = sandboxInstance.proxy as typeof window; - mountSandbox = sandboxInstance.mount; - unmountSandbox = sandboxInstance.unmount; + global = sandboxContainer.instance.proxy as typeof window; + mountSandbox = sandboxContainer.mount; + unmountSandbox = sandboxContainer.unmount; } const { beforeUnmount = [], afterUnmount = [], afterMount = [], beforeMount = [], beforeLoad = [] } = mergeWith( @@ -307,7 +323,12 @@ export async function loadApp( // get the lifecycle hooks from module exports const scriptExports: any = await execScripts(global, !useLooseSandbox); - const { bootstrap, mount, unmount, update } = getLifecyclesFromExports(scriptExports, appName, global); + const { bootstrap, mount, unmount, update } = getLifecyclesFromExports( + scriptExports, + appName, + global, + sandboxContainer?.instance?.latestSetProp, + ); const { onGlobalStateChange, diff --git a/src/sandbox/index.ts b/src/sandbox/index.ts index 16e37de..f69126b 100644 --- a/src/sandbox/index.ts +++ b/src/sandbox/index.ts @@ -28,7 +28,7 @@ export { css } from './patchers'; * @param useLooseSandbox * @param excludeAssetFilter */ -export function createSandbox( +export function createSandboxContainer( appName: string, elementGetter: () => HTMLElement | ShadowRoot, scopedCSS: boolean, @@ -50,7 +50,7 @@ export function createSandbox( let sideEffectsRebuilders: Rebuilder[] = []; return { - proxy: sandbox.proxy, + instance: sandbox, /** * 沙箱被 mount diff --git a/src/sandbox/legacy/sandbox.ts b/src/sandbox/legacy/sandbox.ts index 572a7b6..384389c 100644 --- a/src/sandbox/legacy/sandbox.ts +++ b/src/sandbox/legacy/sandbox.ts @@ -41,6 +41,8 @@ export default class SingularProxySandbox implements SandBox { sandboxRunning = true; + latestSetProp: PropertyKey | null = null; + active() { if (!this.sandboxRunning) { this.currentUpdatedPropsValueMap.forEach((v, p) => setWindowProp(p, v)); @@ -90,6 +92,8 @@ export default class SingularProxySandbox implements SandBox { // eslint-disable-next-line no-param-reassign (rawWindow as any)[p] = value; + self.latestSetProp = p; + return true; } diff --git a/src/sandbox/proxySandbox.ts b/src/sandbox/proxySandbox.ts index 7f00146..ee98462 100644 --- a/src/sandbox/proxySandbox.ts +++ b/src/sandbox/proxySandbox.ts @@ -136,6 +136,8 @@ export default class ProxySandbox implements SandBox { sandboxRunning = true; + latestSetProp: PropertyKey | null = null; + active() { if (!this.sandboxRunning) activeSandboxCount++; this.sandboxRunning = true; @@ -199,6 +201,8 @@ export default class ProxySandbox implements SandBox { updatedValueSet.add(p); + self.latestSetProp = p; + return true; }