✨ 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
|
* 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;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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) => {
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user