️reduce the invocation times with next tick in sandbox to make qiankun performance in control (#1629)

This commit is contained in:
Kuitos 2021-08-04 13:05:32 +08:00 committed by GitHub
parent 9e4274e3f8
commit d7e96a02ae
4 changed files with 38 additions and 7 deletions

View File

@ -3,6 +3,7 @@ import {
getDefaultTplWrapper,
getWrapperId,
getXPathForElement,
nextTask,
sleep,
validateExportLifecycle,
} from '../utils';
@ -116,3 +117,20 @@ test('should getXPathForElement work well', () => {
const xpath1 = getXPathForElement(virtualDOM, document);
expect(xpath1).toBeUndefined();
});
it('should nextTick just executed once in one task context', async () => {
let counter = 0;
nextTask(() => ++counter);
nextTask(() => ++counter);
nextTask(() => ++counter);
nextTask(() => ++counter);
await sleep(0);
expect(counter).toBe(1);
await sleep(0);
nextTask(() => ++counter);
await sleep(0);
nextTask(() => ++counter);
await sleep(0);
expect(counter).toBe(3);
});

View File

@ -11,6 +11,7 @@ declare global {
__POWERED_BY_QIANKUN__?: boolean;
__INJECTED_PUBLIC_PATH_BY_QIANKUN__?: string;
__QIANKUN_DEVELOPMENT__?: boolean;
Zone?: CallableFunction;
}
}

View File

@ -5,7 +5,7 @@
*/
import type { SandBox } from '../interfaces';
import { SandBoxType } from '../interfaces';
import { nextTick } from '../utils';
import { nextTask } from '../utils';
import { getTargetValue, setCurrentRunningSandboxProxy } from './common';
/**
@ -215,13 +215,13 @@ export default class ProxySandbox implements SandBox {
},
get(target: FakeWindow, p: PropertyKey): any {
if (p === Symbol.unscopables) return unscopables;
setCurrentRunningSandboxProxy(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
nextTick(() => setCurrentRunningSandboxProxy(null));
nextTask(() => setCurrentRunningSandboxProxy(null));
if (p === Symbol.unscopables) return unscopables;
// 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

View File

@ -14,12 +14,24 @@ export function sleep(ms: number) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
// Promise.then might be synchronized in Zone.js context, we need to use setTimeout instead to mock next tick.
const nextTick: (cb: () => void) => void =
typeof window.Zone === 'function' ? setTimeout : (cb) => Promise.resolve().then(cb);
let globalTaskPending = false;
/**
* run a callback after next tick
* Run a callback before next task executing, and the invocation is idempotent in every singular task
* That means even we called nextTask multi times in one task, only the first callback will be pushed to nextTick to be invoked.
* @param cb
*/
export function nextTick(cb: () => void): void {
Promise.resolve().then(cb);
export function nextTask(cb: () => void): void {
if (!globalTaskPending) {
globalTaskPending = true;
nextTick(() => {
cb();
globalTaskPending = false;
});
}
}
const fnRegexCheckCacheMap = new WeakMap<any | FunctionConstructor, boolean>();