From 116f40a2cd86762a003afadc204a2289c7268fdd Mon Sep 17 00:00:00 2001 From: Kuitos Date: Mon, 19 Jul 2021 17:32:02 +0800 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20loose=20sandbox=20support=20mutaion?= =?UTF-8?q?=20with=20defineProperty=20(#1581)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/sandbox/legacy/__tests__/sandbox.test.ts | 27 ++++++++ src/sandbox/legacy/sandbox.ts | 65 ++++++++++++-------- 2 files changed, 67 insertions(+), 25 deletions(-) create mode 100644 src/sandbox/legacy/__tests__/sandbox.test.ts diff --git a/src/sandbox/legacy/__tests__/sandbox.test.ts b/src/sandbox/legacy/__tests__/sandbox.test.ts new file mode 100644 index 0000000..7185e03 --- /dev/null +++ b/src/sandbox/legacy/__tests__/sandbox.test.ts @@ -0,0 +1,27 @@ +/** + * @author Kuitos + * @since 2021-07-19 + */ + +import LooseSandbox from '../sandbox'; + +describe('loose sandbox', () => { + it('should record the mutation from Object.defineProperty', () => { + const sandbox = new LooseSandbox('defineProperty'); + + const { proxy } = sandbox; + + proxy.prop1 = 123; + Object.defineProperty(proxy, 'prop2', { value: 456, configurable: true, writable: true }); + + expect(proxy.prop1).toBe(123); + expect(window.prop1).toBe(123); + expect(proxy.prop2).toBe(456); + expect(window.prop2).toBe(456); + + sandbox.inactive(); + + expect(window.prop1).toBeUndefined(); + expect(window.prop2).toBeUndefined(); + }); +}); diff --git a/src/sandbox/legacy/sandbox.ts b/src/sandbox/legacy/sandbox.ts index 9a72299..042f36a 100644 --- a/src/sandbox/legacy/sandbox.ts +++ b/src/sandbox/legacy/sandbox.ts @@ -76,33 +76,39 @@ export default class SingularProxySandbox implements SandBox { const rawWindow = window; const fakeWindow = Object.create(null) as Window; + const setTrap = (p: PropertyKey, value: any, originalValue: any, sync2Window = true) => { + if (this.sandboxRunning) { + if (!rawWindow.hasOwnProperty(p)) { + addedPropsMapInSandbox.set(p, value); + } else if (!modifiedPropsOriginalValueMapInSandbox.has(p)) { + // 如果当前 window 对象存在该属性,且 record map 中未记录过,则记录该属性初始值 + modifiedPropsOriginalValueMapInSandbox.set(p, originalValue); + } + + currentUpdatedPropsValueMap.set(p, value); + + if (sync2Window) { + // 必须重新设置 window 对象保证下次 get 时能拿到已更新的数据 + (rawWindow as any)[p] = value; + } + + this.latestSetProp = p; + + return true; + } + + if (process.env.NODE_ENV === 'development') { + console.warn(`[qiankun] Set window.${p.toString()} while sandbox destroyed or inactive in ${name}!`); + } + + // 在 strict-mode 下,Proxy 的 handler.set 返回 false 会抛出 TypeError,在沙箱卸载的情况下应该忽略错误 + return true; + }; + const proxy = new Proxy(fakeWindow, { set: (_: Window, p: PropertyKey, value: any): boolean => { - if (this.sandboxRunning) { - if (!rawWindow.hasOwnProperty(p)) { - addedPropsMapInSandbox.set(p, value); - } else if (!modifiedPropsOriginalValueMapInSandbox.has(p)) { - // 如果当前 window 对象存在该属性,且 record map 中未记录过,则记录该属性初始值 - const originalValue = (rawWindow as any)[p]; - modifiedPropsOriginalValueMapInSandbox.set(p, originalValue); - } - - currentUpdatedPropsValueMap.set(p, value); - // 必须重新设置 window 对象保证下次 get 时能拿到已更新的数据 - // eslint-disable-next-line no-param-reassign - (rawWindow as any)[p] = value; - - this.latestSetProp = p; - - return true; - } - - if (process.env.NODE_ENV === 'development') { - console.warn(`[qiankun] Set window.${p.toString()} while sandbox destroyed or inactive in ${name}!`); - } - - // 在 strict-mode 下,Proxy 的 handler.set 返回 false 会抛出 TypeError,在沙箱卸载的情况下应该忽略错误 - return true; + const originalValue = (rawWindow as any)[p]; + return setTrap(p, value, originalValue, true); }, get(_: Window, p: PropertyKey): any { @@ -131,6 +137,15 @@ export default class SingularProxySandbox implements SandBox { } return descriptor; }, + + defineProperty(_: Window, p: string | symbol, attributes: PropertyDescriptor): boolean { + const originalValue = (rawWindow as any)[p]; + const done = Reflect.defineProperty(rawWindow, p, attributes); + const value = (rawWindow as any)[p]; + setTrap(p, value, originalValue, false); + + return done; + }, }); this.proxy = proxy;