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:
Kuitos 2021-09-15 22:54:44 +08:00 committed by GitHub
parent 91a238875b
commit c4b604228a
5 changed files with 42 additions and 32 deletions

View File

@ -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';

View File

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

View File

@ -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';
/** /**
* *

View File

@ -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>(

View File

@ -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];