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 { initGlobalState } from './globalState';
export { getCurrentRunningApp as __internalGetCurrentRunningApp } from './sandbox';
export * from './errorHandler';
export * from './effects';
export * from './interfaces';

View File

@ -5,28 +5,18 @@
import { isBoundedFunction, isCallable, isConstructable } from '../utils';
declare global {
interface Window {
__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 });
type AppInstance = { name: string; window: WindowProxy };
let currentRunningApp: AppInstance | null = null;
/**
* 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() {
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
nativeGlobal.__currentRunningAppInSandbox__ = instance;
currentRunningApp = appInstance;
}
const functionBoundedValueMap = new WeakMap<CallableFunction, CallableFunction>();

View File

@ -9,6 +9,7 @@ import ProxySandbox from './proxySandbox';
import SnapshotSandbox from './snapshotSandbox';
export { css } from './patchers';
export { getCurrentRunningApp } from './common';
/**
*

View File

@ -14,10 +14,25 @@ import {
recordStyledComponentsCSSRules,
} from './common';
const rawDocumentCreateElement = Document.prototype.createElement;
const proxyAttachContainerConfigMap = new WeakMap<WindowProxy, ContainerConfig>();
declare global {
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 rawDocumentCreateElement = Document.prototype.createElement;
function patchDocumentCreateElement() {
if (Document.prototype.createElement === rawDocumentCreateElement) {
Document.prototype.createElement = function createElement<K extends keyof HTMLElementTagNameMap>(

View File

@ -122,16 +122,6 @@ function createFakeWindow(global: Window) {
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
*/
@ -151,6 +141,18 @@ export default class ProxySandbox implements SandBox {
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() {
if (!this.sandboxRunning) activeSandboxCount++;
this.sandboxRunning = true;
@ -190,7 +192,7 @@ export default class ProxySandbox implements SandBox {
const proxy = new Proxy(fakeWindow, {
set: (target: FakeWindow, p: PropertyKey, value: any): boolean => {
if (this.sandboxRunning) {
registerRunningApp(name, proxy);
this.registerRunningApp(name, proxy);
// We must kept its description while the property existed in rawWindow before
if (!target.hasOwnProperty(p) && rawWindow.hasOwnProperty(p)) {
const descriptor = Object.getOwnPropertyDescriptor(rawWindow, p);
@ -228,9 +230,10 @@ export default class ProxySandbox implements SandBox {
return true;
},
get(target: FakeWindow, p: PropertyKey): any {
get: (target: FakeWindow, p: PropertyKey): any => {
this.registerRunningApp(name, proxy);
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
// see https://github.com/eligrey/FileSaver.js/blob/master/src/FileSaver.js#L13
if (p === 'window' || p === 'self') {
@ -330,8 +333,8 @@ export default class ProxySandbox implements SandBox {
}
},
deleteProperty(target: FakeWindow, p: string | number | symbol): boolean {
registerRunningApp(name, proxy);
deleteProperty: (target: FakeWindow, p: string | number | symbol): boolean => {
this.registerRunningApp(name, proxy);
if (target.hasOwnProperty(p)) {
// @ts-ignore
delete target[p];