✨ remove global way for recording current running sandbox to fix nested scenario (#1723)
* ✨ remove global way for recording current running sandbox to support parallel multiple instance *✨ export internal getCurrentRunningApp API Co-authored-by: tli4 <a@tianyi.li>
This commit is contained in:
parent
91a238875b
commit
c4b604228a
|
|
@ -5,6 +5,7 @@
|
||||||
|
|
||||||
export { loadMicroApp, registerMicroApps, start } from './apis';
|
export { loadMicroApp, registerMicroApps, start } from './apis';
|
||||||
export { initGlobalState } from './globalState';
|
export { initGlobalState } from './globalState';
|
||||||
|
export { getCurrentRunningApp as __internalGetCurrentRunningApp } from './sandbox';
|
||||||
export * from './errorHandler';
|
export * from './errorHandler';
|
||||||
export * from './effects';
|
export * from './effects';
|
||||||
export * from './interfaces';
|
export * from './interfaces';
|
||||||
|
|
|
||||||
|
|
@ -5,28 +5,18 @@
|
||||||
|
|
||||||
import { isBoundedFunction, isCallable, isConstructable } from '../utils';
|
import { isBoundedFunction, isCallable, isConstructable } from '../utils';
|
||||||
|
|
||||||
declare global {
|
type AppInstance = { name: string; window: WindowProxy };
|
||||||
interface Window {
|
let currentRunningApp: AppInstance | null = null;
|
||||||
__currentRunningAppInSandbox__: { name: string; window: WindowProxy } | null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get native global window with a sandbox disgusted way, thus we could share it between qiankun instances🤪
|
|
||||||
// eslint-disable-next-line no-new-func
|
|
||||||
const nativeGlobal: Window = new Function('return this')();
|
|
||||||
Object.defineProperty(nativeGlobal, '__currentRunningAppInSandbox__', { enumerable: false, writable: true });
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* get the app that running tasks at current tick
|
* get the app that running tasks at current tick
|
||||||
* @warning this method only works with proxy sandbox, right now it is for internal use only.
|
|
||||||
*/
|
*/
|
||||||
export function getCurrentRunningApp() {
|
export function getCurrentRunningApp() {
|
||||||
return nativeGlobal.__currentRunningAppInSandbox__;
|
return currentRunningApp;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function setCurrentRunningApp(instance: { name: string; window: WindowProxy } | null) {
|
export function setCurrentRunningApp(appInstance: { name: string; window: WindowProxy } | null) {
|
||||||
// set currentRunningApp and it's proxySandbox to global window, as its only use case is for document.createElement from now on, which hijacked by a global way
|
// set currentRunningApp and it's proxySandbox to global window, as its only use case is for document.createElement from now on, which hijacked by a global way
|
||||||
nativeGlobal.__currentRunningAppInSandbox__ = instance;
|
currentRunningApp = appInstance;
|
||||||
}
|
}
|
||||||
|
|
||||||
const functionBoundedValueMap = new WeakMap<CallableFunction, CallableFunction>();
|
const functionBoundedValueMap = new WeakMap<CallableFunction, CallableFunction>();
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import ProxySandbox from './proxySandbox';
|
||||||
import SnapshotSandbox from './snapshotSandbox';
|
import SnapshotSandbox from './snapshotSandbox';
|
||||||
|
|
||||||
export { css } from './patchers';
|
export { css } from './patchers';
|
||||||
|
export { getCurrentRunningApp } from './common';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 生成应用运行时沙箱
|
* 生成应用运行时沙箱
|
||||||
|
|
|
||||||
|
|
@ -14,10 +14,25 @@ import {
|
||||||
recordStyledComponentsCSSRules,
|
recordStyledComponentsCSSRules,
|
||||||
} from './common';
|
} from './common';
|
||||||
|
|
||||||
const rawDocumentCreateElement = Document.prototype.createElement;
|
declare global {
|
||||||
const proxyAttachContainerConfigMap = new WeakMap<WindowProxy, ContainerConfig>();
|
interface Window {
|
||||||
|
__proxyAttachContainerConfigMap__: WeakMap<WindowProxy, ContainerConfig>;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get native global window with a sandbox disgusted way, thus we could share it between qiankun instances🤪
|
||||||
|
// eslint-disable-next-line no-new-func
|
||||||
|
const nativeGlobal: Window = new Function('return this')();
|
||||||
|
Object.defineProperty(nativeGlobal, '__proxyAttachContainerConfigMap__', { enumerable: false, writable: true });
|
||||||
|
|
||||||
|
// Share proxyAttachContainerConfigMap between multiple qiankun instance, thus they could access the same record
|
||||||
|
nativeGlobal.__proxyAttachContainerConfigMap__ =
|
||||||
|
nativeGlobal.__proxyAttachContainerConfigMap__ || new WeakMap<WindowProxy, ContainerConfig>();
|
||||||
|
const proxyAttachContainerConfigMap = nativeGlobal.__proxyAttachContainerConfigMap__;
|
||||||
|
|
||||||
const elementAttachContainerConfigMap = new WeakMap<HTMLElement, ContainerConfig>();
|
const elementAttachContainerConfigMap = new WeakMap<HTMLElement, ContainerConfig>();
|
||||||
|
|
||||||
|
const rawDocumentCreateElement = Document.prototype.createElement;
|
||||||
function patchDocumentCreateElement() {
|
function patchDocumentCreateElement() {
|
||||||
if (Document.prototype.createElement === rawDocumentCreateElement) {
|
if (Document.prototype.createElement === rawDocumentCreateElement) {
|
||||||
Document.prototype.createElement = function createElement<K extends keyof HTMLElementTagNameMap>(
|
Document.prototype.createElement = function createElement<K extends keyof HTMLElementTagNameMap>(
|
||||||
|
|
|
||||||
|
|
@ -122,16 +122,6 @@ function createFakeWindow(global: Window) {
|
||||||
|
|
||||||
let activeSandboxCount = 0;
|
let activeSandboxCount = 0;
|
||||||
|
|
||||||
function registerRunningApp(name: string, proxy: Window) {
|
|
||||||
setCurrentRunningApp({ name, window: proxy });
|
|
||||||
// FIXME if you have any other good ideas
|
|
||||||
// remove the mark in next tick, thus we can identify whether it in micro app or not
|
|
||||||
// this approach is just a workaround, it could not cover all complex cases, such as the micro app runs in the same task context with master in some case
|
|
||||||
nextTask(() => {
|
|
||||||
setCurrentRunningApp(null);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 基于 Proxy 实现的沙箱
|
* 基于 Proxy 实现的沙箱
|
||||||
*/
|
*/
|
||||||
|
|
@ -151,6 +141,18 @@ export default class ProxySandbox implements SandBox {
|
||||||
|
|
||||||
latestSetProp: PropertyKey | null = null;
|
latestSetProp: PropertyKey | null = null;
|
||||||
|
|
||||||
|
private registerRunningApp(name: string, proxy: Window) {
|
||||||
|
if (this.sandboxRunning) {
|
||||||
|
setCurrentRunningApp({ name, window: proxy });
|
||||||
|
// FIXME if you have any other good ideas
|
||||||
|
// remove the mark in next tick, thus we can identify whether it in micro app or not
|
||||||
|
// this approach is just a workaround, it could not cover all complex cases, such as the micro app runs in the same task context with master in some case
|
||||||
|
nextTask(() => {
|
||||||
|
setCurrentRunningApp(null);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
active() {
|
active() {
|
||||||
if (!this.sandboxRunning) activeSandboxCount++;
|
if (!this.sandboxRunning) activeSandboxCount++;
|
||||||
this.sandboxRunning = true;
|
this.sandboxRunning = true;
|
||||||
|
|
@ -190,7 +192,7 @@ export default class ProxySandbox implements SandBox {
|
||||||
const proxy = new Proxy(fakeWindow, {
|
const proxy = new Proxy(fakeWindow, {
|
||||||
set: (target: FakeWindow, p: PropertyKey, value: any): boolean => {
|
set: (target: FakeWindow, p: PropertyKey, value: any): boolean => {
|
||||||
if (this.sandboxRunning) {
|
if (this.sandboxRunning) {
|
||||||
registerRunningApp(name, proxy);
|
this.registerRunningApp(name, proxy);
|
||||||
// We must kept its description while the property existed in rawWindow before
|
// We must kept its description while the property existed in rawWindow before
|
||||||
if (!target.hasOwnProperty(p) && rawWindow.hasOwnProperty(p)) {
|
if (!target.hasOwnProperty(p) && rawWindow.hasOwnProperty(p)) {
|
||||||
const descriptor = Object.getOwnPropertyDescriptor(rawWindow, p);
|
const descriptor = Object.getOwnPropertyDescriptor(rawWindow, p);
|
||||||
|
|
@ -228,9 +230,10 @@ export default class ProxySandbox implements SandBox {
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
|
|
||||||
get(target: FakeWindow, p: PropertyKey): any {
|
get: (target: FakeWindow, p: PropertyKey): any => {
|
||||||
|
this.registerRunningApp(name, proxy);
|
||||||
|
|
||||||
if (p === Symbol.unscopables) return unscopables;
|
if (p === Symbol.unscopables) return unscopables;
|
||||||
registerRunningApp(name, proxy);
|
|
||||||
// avoid who using window.window or window.self to escape the sandbox environment to touch the really window
|
// avoid who using window.window or window.self to escape the sandbox environment to touch the really window
|
||||||
// see https://github.com/eligrey/FileSaver.js/blob/master/src/FileSaver.js#L13
|
// see https://github.com/eligrey/FileSaver.js/blob/master/src/FileSaver.js#L13
|
||||||
if (p === 'window' || p === 'self') {
|
if (p === 'window' || p === 'self') {
|
||||||
|
|
@ -330,8 +333,8 @@ export default class ProxySandbox implements SandBox {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
deleteProperty(target: FakeWindow, p: string | number | symbol): boolean {
|
deleteProperty: (target: FakeWindow, p: string | number | symbol): boolean => {
|
||||||
registerRunningApp(name, proxy);
|
this.registerRunningApp(name, proxy);
|
||||||
if (target.hasOwnProperty(p)) {
|
if (target.hasOwnProperty(p)) {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
delete target[p];
|
delete target[p];
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user