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
*/
excludeAssetFilter?: (url: string) => boolean;
globalContext?: typeof window;
};
export type FrameworkConfiguration = QiankunSpecialOpts & ImportEntryOpts & StartOpts;

View File

@ -254,7 +254,13 @@ export async function loadApp<T extends ObjectType>(
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<T extends ObjectType>(
() => 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<T extends ObjectType>(
scopedCSS,
useLooseSandbox,
excludeAssetFilter,
global,
);
// 用沙箱的代理对象作为接下来使用的全局对象
global = sandboxContainer.instance.proxy as typeof window;

View File

@ -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);
}

View File

@ -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<PropertyKey, any>();
@ -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) => {

View File

@ -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<PropertyKey, SymbolTarget>();
@ -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;
}