support nest sandbox scenario (#1722)

This commit is contained in:
Kuitos 2021-09-15 20:53:17 +08:00 committed by GitHub
parent 63c177c780
commit 91a238875b
5 changed files with 42 additions and 23 deletions

View File

@ -86,6 +86,8 @@ type QiankunSpecialOpts = {
* skip some scripts or links intercept, like JSONP * skip some scripts or links intercept, like JSONP
*/ */
excludeAssetFilter?: (url: string) => boolean; excludeAssetFilter?: (url: string) => boolean;
globalContext?: typeof window;
}; };
export type FrameworkConfiguration = QiankunSpecialOpts & ImportEntryOpts & StartOpts; export type FrameworkConfiguration = QiankunSpecialOpts & ImportEntryOpts & StartOpts;

View File

@ -254,7 +254,13 @@ export async function loadApp<T extends ObjectType>(
performanceMark(markName); 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 // get the entry html content and script executor
const { template, execScripts, assetPublicPath } = await importEntry(entry, importEntryOpts); const { template, execScripts, assetPublicPath } = await importEntry(entry, importEntryOpts);
@ -295,7 +301,7 @@ export async function loadApp<T extends ObjectType>(
() => initialAppWrapperElement, () => initialAppWrapperElement,
); );
let global = window; let global = globalContext;
let mountSandbox = () => Promise.resolve(); let mountSandbox = () => Promise.resolve();
let unmountSandbox = () => Promise.resolve(); let unmountSandbox = () => Promise.resolve();
const useLooseSandbox = typeof sandbox === 'object' && !!sandbox.loose; const useLooseSandbox = typeof sandbox === 'object' && !!sandbox.loose;
@ -308,6 +314,7 @@ export async function loadApp<T extends ObjectType>(
scopedCSS, scopedCSS,
useLooseSandbox, useLooseSandbox,
excludeAssetFilter, excludeAssetFilter,
global,
); );
// 用沙箱的代理对象作为接下来使用的全局对象 // 用沙箱的代理对象作为接下来使用的全局对象
global = sandboxContainer.instance.proxy as typeof window; global = sandboxContainer.instance.proxy as typeof window;

View File

@ -27,6 +27,7 @@ export { css } from './patchers';
* @param scopedCSS * @param scopedCSS
* @param useLooseSandbox * @param useLooseSandbox
* @param excludeAssetFilter * @param excludeAssetFilter
* @param globalContext
*/ */
export function createSandboxContainer( export function createSandboxContainer(
appName: string, appName: string,
@ -34,10 +35,11 @@ export function createSandboxContainer(
scopedCSS: boolean, scopedCSS: boolean,
useLooseSandbox?: boolean, useLooseSandbox?: boolean,
excludeAssetFilter?: (url: string) => boolean, excludeAssetFilter?: (url: string) => boolean,
globalContext?: typeof window,
) { ) {
let sandbox: SandBox; let sandbox: SandBox;
if (window.Proxy) { if (window.Proxy) {
sandbox = useLooseSandbox ? new LegacySandbox(appName) : new ProxySandbox(appName); sandbox = useLooseSandbox ? new LegacySandbox(appName, globalContext) : new ProxySandbox(appName, globalContext);
} else { } else {
sandbox = new SnapshotSandbox(appName); sandbox = new SnapshotSandbox(appName);
} }

View File

@ -6,25 +6,16 @@ import type { SandBox } from '../../interfaces';
import { SandBoxType } from '../../interfaces'; import { SandBoxType } from '../../interfaces';
import { getTargetValue } from '../common'; import { getTargetValue } from '../common';
function isPropConfigurable(target: typeof window, prop: PropertyKey) { function isPropConfigurable(target: WindowProxy, prop: PropertyKey) {
const descriptor = Object.getOwnPropertyDescriptor(target, prop); const descriptor = Object.getOwnPropertyDescriptor(target, prop);
return descriptor ? descriptor.configurable : true; 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 * Proxy
* TODO: 为了兼容性 singular 使 * TODO: 为了兼容性 singular 使
*/ */
export default class SingularProxySandbox implements SandBox { export default class LegacySandbox implements SandBox {
/** 沙箱期间新增的全局变量 */ /** 沙箱期间新增的全局变量 */
private addedPropsMapInSandbox = new Map<PropertyKey, any>(); private addedPropsMapInSandbox = new Map<PropertyKey, any>();
@ -38,15 +29,28 @@ export default class SingularProxySandbox implements SandBox {
proxy: WindowProxy; proxy: WindowProxy;
globalContext: typeof window;
type: SandBoxType; type: SandBoxType;
sandboxRunning = true; sandboxRunning = true;
latestSetProp: PropertyKey | null = null; 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() { active() {
if (!this.sandboxRunning) { if (!this.sandboxRunning) {
this.currentUpdatedPropsValueMap.forEach((v, p) => setWindowProp(p, v)); this.currentUpdatedPropsValueMap.forEach((v, p) => this.setWindowProp(p, v));
} }
this.sandboxRunning = true; this.sandboxRunning = true;
@ -62,18 +66,19 @@ export default class SingularProxySandbox implements SandBox {
// renderSandboxSnapshot = snapshot(currentUpdatedPropsValueMapForSnapshot); // renderSandboxSnapshot = snapshot(currentUpdatedPropsValueMapForSnapshot);
// restore global props to initial snapshot // restore global props to initial snapshot
this.modifiedPropsOriginalValueMapInSandbox.forEach((v, p) => setWindowProp(p, v)); this.modifiedPropsOriginalValueMapInSandbox.forEach((v, p) => this.setWindowProp(p, v));
this.addedPropsMapInSandbox.forEach((_, p) => setWindowProp(p, undefined, true)); this.addedPropsMapInSandbox.forEach((_, p) => this.setWindowProp(p, undefined, true));
this.sandboxRunning = false; this.sandboxRunning = false;
} }
constructor(name: string) { constructor(name: string, globalContext = window) {
this.name = name; this.name = name;
this.globalContext = globalContext;
this.type = SandBoxType.LegacyProxy; this.type = SandBoxType.LegacyProxy;
const { addedPropsMapInSandbox, modifiedPropsOriginalValueMapInSandbox, currentUpdatedPropsValueMap } = this; const { addedPropsMapInSandbox, modifiedPropsOriginalValueMapInSandbox, currentUpdatedPropsValueMap } = this;
const rawWindow = window; const rawWindow = globalContext;
const fakeWindow = Object.create(null) as Window; const fakeWindow = Object.create(null) as Window;
const setTrap = (p: PropertyKey, value: any, originalValue: any, sync2Window = true) => { const setTrap = (p: PropertyKey, value: any, originalValue: any, sync2Window = true) => {

View File

@ -145,6 +145,8 @@ export default class ProxySandbox implements SandBox {
proxy: WindowProxy; proxy: WindowProxy;
globalContext: typeof window;
sandboxRunning = true; sandboxRunning = true;
latestSetProp: PropertyKey | null = null; latestSetProp: PropertyKey | null = null;
@ -165,7 +167,7 @@ export default class ProxySandbox implements SandBox {
variableWhiteList.forEach((p) => { variableWhiteList.forEach((p) => {
if (this.proxy.hasOwnProperty(p)) { if (this.proxy.hasOwnProperty(p)) {
// @ts-ignore // @ts-ignore
delete window[p]; delete this.globalContext[p];
} }
}); });
} }
@ -173,12 +175,13 @@ export default class ProxySandbox implements SandBox {
this.sandboxRunning = false; this.sandboxRunning = false;
} }
constructor(name: string) { constructor(name: string, globalContext = window) {
this.name = name; this.name = name;
this.globalContext = globalContext;
this.type = SandBoxType.Proxy; this.type = SandBoxType.Proxy;
const { updatedValueSet } = this; const { updatedValueSet } = this;
const rawWindow = window; const rawWindow = globalContext;
const { fakeWindow, propertiesWithGetter } = createFakeWindow(rawWindow); const { fakeWindow, propertiesWithGetter } = createFakeWindow(rawWindow);
const descriptorTargetMap = new Map<PropertyKey, SymbolTarget>(); const descriptorTargetMap = new Map<PropertyKey, SymbolTarget>();
@ -234,7 +237,7 @@ export default class ProxySandbox implements SandBox {
return proxy; return proxy;
} }
// hijack global accessing with globalThis keyword // hijack globalWindow accessing with globalThis keyword
if (p === 'globalThis') { if (p === 'globalThis') {
return proxy; return proxy;
} }