From 91a238875b35b3b635946ae623656dcfbc94d345 Mon Sep 17 00:00:00 2001 From: Kuitos Date: Wed, 15 Sep 2021 20:53:17 +0800 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20support=20nest=20sandbox=20scenario?= =?UTF-8?q?=20(#1722)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/interfaces.ts | 2 ++ src/loader.ts | 11 +++++++++-- src/sandbox/index.ts | 4 +++- src/sandbox/legacy/sandbox.ts | 37 ++++++++++++++++++++--------------- src/sandbox/proxySandbox.ts | 11 +++++++---- 5 files changed, 42 insertions(+), 23 deletions(-) diff --git a/src/interfaces.ts b/src/interfaces.ts index 2eff75d..6dd1e28 100644 --- a/src/interfaces.ts +++ b/src/interfaces.ts @@ -86,6 +86,8 @@ type QiankunSpecialOpts = { * skip some scripts or links intercept, like JSONP */ excludeAssetFilter?: (url: string) => boolean; + + globalContext?: typeof window; }; export type FrameworkConfiguration = QiankunSpecialOpts & ImportEntryOpts & StartOpts; diff --git a/src/loader.ts b/src/loader.ts index b0c126f..828c7f0 100644 --- a/src/loader.ts +++ b/src/loader.ts @@ -254,7 +254,13 @@ export async function loadApp( performanceMark(markName); } - const { singular = false, sandbox = true, excludeAssetFilter, ...importEntryOpts } = configuration; + const { + singular = false, + sandbox = true, + excludeAssetFilter, + globalContext = window, + ...importEntryOpts + } = configuration; // get the entry html content and script executor const { template, execScripts, assetPublicPath } = await importEntry(entry, importEntryOpts); @@ -295,7 +301,7 @@ export async function loadApp( () => initialAppWrapperElement, ); - let global = window; + let global = globalContext; let mountSandbox = () => Promise.resolve(); let unmountSandbox = () => Promise.resolve(); const useLooseSandbox = typeof sandbox === 'object' && !!sandbox.loose; @@ -308,6 +314,7 @@ export async function loadApp( scopedCSS, useLooseSandbox, excludeAssetFilter, + global, ); // 用沙箱的代理对象作为接下来使用的全局对象 global = sandboxContainer.instance.proxy as typeof window; diff --git a/src/sandbox/index.ts b/src/sandbox/index.ts index 4ff1f76..bc7f62e 100644 --- a/src/sandbox/index.ts +++ b/src/sandbox/index.ts @@ -27,6 +27,7 @@ export { css } from './patchers'; * @param scopedCSS * @param useLooseSandbox * @param excludeAssetFilter + * @param globalContext */ export function createSandboxContainer( appName: string, @@ -34,10 +35,11 @@ export function createSandboxContainer( scopedCSS: boolean, useLooseSandbox?: boolean, excludeAssetFilter?: (url: string) => boolean, + globalContext?: typeof window, ) { let sandbox: SandBox; if (window.Proxy) { - sandbox = useLooseSandbox ? new LegacySandbox(appName) : new ProxySandbox(appName); + sandbox = useLooseSandbox ? new LegacySandbox(appName, globalContext) : new ProxySandbox(appName, globalContext); } else { sandbox = new SnapshotSandbox(appName); } diff --git a/src/sandbox/legacy/sandbox.ts b/src/sandbox/legacy/sandbox.ts index 042f36a..c496b4c 100644 --- a/src/sandbox/legacy/sandbox.ts +++ b/src/sandbox/legacy/sandbox.ts @@ -6,25 +6,16 @@ import type { SandBox } from '../../interfaces'; import { SandBoxType } from '../../interfaces'; import { getTargetValue } from '../common'; -function isPropConfigurable(target: typeof window, prop: PropertyKey) { +function isPropConfigurable(target: WindowProxy, prop: PropertyKey) { const descriptor = Object.getOwnPropertyDescriptor(target, prop); return descriptor ? descriptor.configurable : true; } -function setWindowProp(prop: PropertyKey, value: any, toDelete?: boolean) { - if (value === undefined && toDelete) { - delete (window as any)[prop]; - } else if (isPropConfigurable(window, prop) && typeof prop !== 'symbol') { - Object.defineProperty(window, prop, { writable: true, configurable: true }); - (window as any)[prop] = value; - } -} - /** * 基于 Proxy 实现的沙箱 * TODO: 为了兼容性 singular 模式下依旧使用该沙箱,等新沙箱稳定之后再切换 */ -export default class SingularProxySandbox implements SandBox { +export default class LegacySandbox implements SandBox { /** 沙箱期间新增的全局变量 */ private addedPropsMapInSandbox = new Map(); @@ -38,15 +29,28 @@ export default class SingularProxySandbox implements SandBox { proxy: WindowProxy; + globalContext: typeof window; + type: SandBoxType; sandboxRunning = true; latestSetProp: PropertyKey | null = null; + private setWindowProp(prop: PropertyKey, value: any, toDelete?: boolean) { + if (value === undefined && toDelete) { + // eslint-disable-next-line no-param-reassign + delete (this.globalContext as any)[prop]; + } else if (isPropConfigurable(this.globalContext, prop) && typeof prop !== 'symbol') { + Object.defineProperty(this.globalContext, prop, { writable: true, configurable: true }); + // eslint-disable-next-line no-param-reassign + (this.globalContext as any)[prop] = value; + } + } + active() { if (!this.sandboxRunning) { - this.currentUpdatedPropsValueMap.forEach((v, p) => setWindowProp(p, v)); + this.currentUpdatedPropsValueMap.forEach((v, p) => this.setWindowProp(p, v)); } this.sandboxRunning = true; @@ -62,18 +66,19 @@ export default class SingularProxySandbox implements SandBox { // renderSandboxSnapshot = snapshot(currentUpdatedPropsValueMapForSnapshot); // restore global props to initial snapshot - this.modifiedPropsOriginalValueMapInSandbox.forEach((v, p) => setWindowProp(p, v)); - this.addedPropsMapInSandbox.forEach((_, p) => setWindowProp(p, undefined, true)); + this.modifiedPropsOriginalValueMapInSandbox.forEach((v, p) => this.setWindowProp(p, v)); + this.addedPropsMapInSandbox.forEach((_, p) => this.setWindowProp(p, undefined, true)); this.sandboxRunning = false; } - constructor(name: string) { + constructor(name: string, globalContext = window) { this.name = name; + this.globalContext = globalContext; this.type = SandBoxType.LegacyProxy; const { addedPropsMapInSandbox, modifiedPropsOriginalValueMapInSandbox, currentUpdatedPropsValueMap } = this; - const rawWindow = window; + const rawWindow = globalContext; const fakeWindow = Object.create(null) as Window; const setTrap = (p: PropertyKey, value: any, originalValue: any, sync2Window = true) => { diff --git a/src/sandbox/proxySandbox.ts b/src/sandbox/proxySandbox.ts index 6d35bcd..7a58a12 100644 --- a/src/sandbox/proxySandbox.ts +++ b/src/sandbox/proxySandbox.ts @@ -145,6 +145,8 @@ export default class ProxySandbox implements SandBox { proxy: WindowProxy; + globalContext: typeof window; + sandboxRunning = true; latestSetProp: PropertyKey | null = null; @@ -165,7 +167,7 @@ export default class ProxySandbox implements SandBox { variableWhiteList.forEach((p) => { if (this.proxy.hasOwnProperty(p)) { // @ts-ignore - delete window[p]; + delete this.globalContext[p]; } }); } @@ -173,12 +175,13 @@ export default class ProxySandbox implements SandBox { this.sandboxRunning = false; } - constructor(name: string) { + constructor(name: string, globalContext = window) { this.name = name; + this.globalContext = globalContext; this.type = SandBoxType.Proxy; const { updatedValueSet } = this; - const rawWindow = window; + const rawWindow = globalContext; const { fakeWindow, propertiesWithGetter } = createFakeWindow(rawWindow); const descriptorTargetMap = new Map(); @@ -234,7 +237,7 @@ export default class ProxySandbox implements SandBox { return proxy; } - // hijack global accessing with globalThis keyword + // hijack globalWindow accessing with globalThis keyword if (p === 'globalThis') { return proxy; }