diff --git a/packages/wujie-core/__test__/integration/common.ts b/packages/wujie-core/__test__/integration/common.ts index 21b26c4c3..c854fb11c 100644 --- a/packages/wujie-core/__test__/integration/common.ts +++ b/packages/wujie-core/__test__/integration/common.ts @@ -136,6 +136,8 @@ export const reactMainAppInfoMap = { }, }; export const reactMainAppInfoList = Object.entries(reactMainAppInfoMap).map((item) => item[1]); +export const reactMainAppNameList = Object.entries(reactMainAppInfoMap).map((item) => item[0]); + export const vueMainAppInfoMap = { react16: { name: "react16", @@ -275,3 +277,4 @@ export const vueMainAppInfoMap = { }, }; export const vueMainAppInfoList = Object.entries(vueMainAppInfoMap).map((item) => item[1]); +export const vueMainAppNameList = Object.entries(vueMainAppInfoMap).map((item) => item[0]); diff --git a/packages/wujie-core/__test__/integration/proxy.test.ts b/packages/wujie-core/__test__/integration/proxy.test.ts new file mode 100644 index 000000000..b0a83d8d3 --- /dev/null +++ b/packages/wujie-core/__test__/integration/proxy.test.ts @@ -0,0 +1,62 @@ +import { awaitConsoleLogMessage } from "./utils"; +import { reactMainAppInfoMap, vueMainAppInfoMap, vueMainAppNameList, reactMainAppNameList } from "./common"; + +const generateTest = ( + AppInfoMap: typeof reactMainAppInfoMap | typeof vueMainAppInfoMap, + AppNameList: typeof vueMainAppNameList | typeof reactMainAppNameList +) => { + AppNameList.slice(0, 5).forEach((appName) => { + it("proxy test", async () => { + const childApplicationMountedPromise = awaitConsoleLogMessage(page, AppInfoMap[appName].mountedMessage); + await page.click(AppInfoMap[appName].linkSelector); + await childApplicationMountedPromise; + + // 测试boundValue缓存,及作用域 + const { targetCurrentAttribute, isSameBoundFn } = await page.evaluate((childName) => { + const childWindowCollection = [window[0], window[1], window[2], window[3], window[4], window[5]]; + const childWindow: any = childWindowCollection.find((itemWindow) => itemWindow.name === childName); + const currentObject: any = {}; + const childProxyWindow = childWindow.__WUJIE.proxy; + childProxyWindow.addAttributeToObject = function addAttributeToObject() { + this.currentAttribute = "Add attribute"; + }; + childProxyWindow.addAttributeToObject.call(currentObject); + return { + targetCurrentAttribute: currentObject.currentAttribute, + isSameBoundFn: childProxyWindow.setTimeout === childProxyWindow.setTimeout, + }; + }, appName); + + expect(targetCurrentAttribute).toBe("Add attribute"); + expect(isSameBoundFn).toBe(true); + }); + }); +}; + +describe("main react startApp", () => { + beforeAll(async () => { + await page.evaluateOnNewDocument(() => { + // 关闭预加载 + localStorage.clear(); + localStorage.setItem("preload", "false"); + localStorage.setItem("degrade", "false"); + }); + await page.goto("http://localhost:7700/"); + }); + + generateTest(reactMainAppInfoMap, reactMainAppNameList); +}); + +describe("main vue startApp", () => { + beforeAll(async () => { + await page.evaluateOnNewDocument(() => { + // 关闭预加载 + localStorage.clear(); + localStorage.setItem("preload", "false"); + localStorage.setItem("degrade", "false"); + }); + await page.goto("http://localhost:8000/"); + }); + + generateTest(vueMainAppInfoMap, vueMainAppNameList); +}); diff --git a/packages/wujie-core/src/proxy.ts b/packages/wujie-core/src/proxy.ts index dfd61287b..2c87a8c26 100644 --- a/packages/wujie-core/src/proxy.ts +++ b/packages/wujie-core/src/proxy.ts @@ -3,7 +3,14 @@ import { renderElementToContainer } from "./shadow"; import { pushUrlToWindow } from "./sync"; import { documentProxyProperties, rawDocumentQuerySelector } from "./common"; import { WUJIE_TIPS_RELOAD_DISABLED } from "./constant"; -import { getTargetValue, anchorElementGenerator, getDegradeIframe, isCallable, warn } from "./utils"; +import { + getTargetValue, + anchorElementGenerator, + getDegradeIframe, + isCallable, + checkProxyFunction, + warn, +} from "./utils"; /** * location href 的set劫持操作 @@ -58,6 +65,7 @@ export function proxyGenerator( }, set: (target: Window, p: PropertyKey, value: any) => { + checkProxyFunction(value); target[p] = value; return true; }, diff --git a/packages/wujie-core/src/utils.ts b/packages/wujie-core/src/utils.ts index 79803d46c..c73fef086 100644 --- a/packages/wujie-core/src/utils.ts +++ b/packages/wujie-core/src/utils.ts @@ -73,10 +73,23 @@ export function isConstructable(fn: () => any | FunctionConstructor) { return constructable; } +const setFnCacheMap = new WeakMap(); +export function checkProxyFunction(value: any) { + if (isCallable(value) && !isBoundedFunction(value) && !isConstructable(value)) { + if (!setFnCacheMap.has(value)) { + setFnCacheMap.set(value, value); + } + } +} + export function getTargetValue(target: any, p: any): any { const value = target[p]; + if (setFnCacheMap.has(value)) { + return setFnCacheMap.get(value); + } if (isCallable(value) && !isBoundedFunction(value) && !isConstructable(value)) { const boundValue = Function.prototype.bind.call(value, target); + setFnCacheMap.set(value, boundValue); for (const key in value) { boundValue[key] = value[key];