✨ support nest sandbox scenario (#1722)
This commit is contained in:
parent
63c177c780
commit
91a238875b
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) => {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user