From b996a79f754e81b4db71cb4cbf47d39902f45ae9 Mon Sep 17 00:00:00 2001 From: futengda Date: Mon, 7 Jun 2021 18:28:41 +0800 Subject: [PATCH 1/7] =?UTF-8?q?fix:=20=E5=B0=9D=E8=AF=95=E4=BF=AE=E5=A4=8D?= =?UTF-8?q?=E5=B5=8C=E5=85=A5=E4=B8=BB=E5=BA=94=E7=94=A8=E7=9A=84=E5=AD=90?= =?UTF-8?q?=E5=BA=94=E7=94=A8=E7=9A=84=E7=83=AD=E6=9B=B4=E6=96=B0=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 当前问题在于, 在 [#418](https://github.com/umijs/plugins/pull/418) 中, 将clientRenderOpts改为clientRenderOptsStack, 并以pop的方式获取, 这样有两个问题: 1. 当热更新时, 由于已经pop过, 此时clientRenderOptsStack为空, 那么src中生成的`.umi/umi.ts`的modifyClientRenderOpts操作将无法修改rootElement为dom, 而是保持为string. 这就导致热更新时, 直接把子应用挂载到了string类型的rootElement对应的dom上, 也就是最外层的那个root, 就相当于子应用把主应用覆盖了. 2. 在 [#423](https://github.com/umijs/plugins/pull/423) 讲到, mount顺序是一定的, 但是render不是. 又因为上述pop过程是在render过程中进行的, 那么这个pop的顺序也不一定是安全的. 这里使用appId的方式来保证render和clientRenderOpts的一致性. --- packages/plugin-qiankun/src/slave/index.ts | 15 ++++++++++++++- .../plugin-qiankun/src/slave/lifecycles.ts.tpl | 10 +++++++--- .../src/slave/slaveRuntimePlugin.ts.tpl | 8 ++++---- 3 files changed, 25 insertions(+), 8 deletions(-) diff --git a/packages/plugin-qiankun/src/slave/index.ts b/packages/plugin-qiankun/src/slave/index.ts index 19f3efd59..02185c824 100644 --- a/packages/plugin-qiankun/src/slave/index.ts +++ b/packages/plugin-qiankun/src/slave/index.ts @@ -138,11 +138,24 @@ export default function (api: IApi) { '{ genMount as qiankun_genMount, genBootstrap as qiankun_genBootstrap, genUnmount as qiankun_genUnmount, genUpdate as qiankun_genUpdate }', }; }); + + api.addEntryCodeAhead( + () => + ` + const appId = window.proxy.appId = 'appId' in window.proxy ? window.proxy.appId + 1 : 0; + try { + pluginArgs.appId = appId + } catch { + console.error(\`plugin-qiankun: Cannot find pluginArgs object in current scope, please check if the umijs version is greater than xxx\`) + } + `, + ); + api.addEntryCode( () => ` export const bootstrap = qiankun_genBootstrap(clientRender); - export const mount = qiankun_genMount('${api.config.mountElementId}'); + export const mount = qiankun_genMount('${api.config.mountElementId}', appId); export const unmount = qiankun_genUnmount('${api.config.mountElementId}'); export const update = qiankun_genUpdate(); diff --git a/packages/plugin-qiankun/src/slave/lifecycles.ts.tpl b/packages/plugin-qiankun/src/slave/lifecycles.ts.tpl index 7abcb4538..f4c23de20 100644 --- a/packages/plugin-qiankun/src/slave/lifecycles.ts.tpl +++ b/packages/plugin-qiankun/src/slave/lifecycles.ts.tpl @@ -23,7 +23,11 @@ let render = noop; let hasMountedAtLeastOnce = false; export default () => defer.promise; -export const clientRenderOptsStack: any[] = []; + +const clientRenderOptsMap: any = {}; +export function getClientRenderOpts(appId: number) { + return { ...clientRenderOptsMap[appId] }; +} function normalizeHistory( history?: 'string' | Record, @@ -64,7 +68,7 @@ export function genBootstrap(oldRender: typeof noop) { }; } -export function genMount(mountElementId: string) { +export function genMount(mountElementId: string, appId: number) { return async (props?: any) => { // props 有值时说明应用是通过 lifecycle 被主应用唤醒的,而不是独立运行时自己 mount if (typeof props !== 'undefined') { @@ -108,7 +112,7 @@ export function genMount(mountElementId: string) { }, }; - clientRenderOptsStack.push(clientRenderOpts); + clientRenderOptsMap[appId] = clientRenderOpts; } // 第一次 mount defer 被 resolve 后umi 会自动触发 render,非第一次 mount 则需手动触发 diff --git a/packages/plugin-qiankun/src/slave/slaveRuntimePlugin.ts.tpl b/packages/plugin-qiankun/src/slave/slaveRuntimePlugin.ts.tpl index c0aefd4d7..934c2c247 100644 --- a/packages/plugin-qiankun/src/slave/slaveRuntimePlugin.ts.tpl +++ b/packages/plugin-qiankun/src/slave/slaveRuntimePlugin.ts.tpl @@ -1,5 +1,5 @@ import React from 'react'; -import qiankunRender, { clientRenderOptsStack } from './lifecycles'; +import qiankunRender, { getClientRenderOpts } from './lifecycles'; export function rootContainer(container: HTMLElement) { const value = @@ -13,9 +13,9 @@ export const render = (oldRender: any) => { return qiankunRender().then(oldRender); }; -export function modifyClientRenderOpts(memo: any) { - // 每次应用 render 的时候会调 modifyClientRenderOpts,这时尝试从队列中取 render 的配置 - const clientRenderOpts = clientRenderOptsStack.shift(); +export function modifyClientRenderOpts(memo: any, args: any) { + // 每次应用 render 的时候会调 modifyClientRenderOpts,这时根据当前的appId获取 render 的配置 + const clientRenderOpts = getClientRenderOpts(args.appId); if (clientRenderOpts) { const history = clientRenderOpts.getHistory(); delete clientRenderOpts.getHistory; From ce41646d5f5427930e360fabd5b161d6d87d8dfe Mon Sep 17 00:00:00 2001 From: futengda Date: Mon, 7 Jun 2021 18:49:37 +0800 Subject: [PATCH 2/7] fix: typo --- packages/plugin-qiankun/src/slave/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/plugin-qiankun/src/slave/index.ts b/packages/plugin-qiankun/src/slave/index.ts index 02185c824..4e72c7bbf 100644 --- a/packages/plugin-qiankun/src/slave/index.ts +++ b/packages/plugin-qiankun/src/slave/index.ts @@ -144,9 +144,9 @@ export default function (api: IApi) { ` const appId = window.proxy.appId = 'appId' in window.proxy ? window.proxy.appId + 1 : 0; try { - pluginArgs.appId = appId + pluginArgs.appId = appId; } catch { - console.error(\`plugin-qiankun: Cannot find pluginArgs object in current scope, please check if the umijs version is greater than xxx\`) + console.error(\`plugin-qiankun: Cannot find pluginArgs object in current scope, please check if the umijs version is greater than xxx\`); } `, ); From a80e4533fb10bc96147d142e5d6866d6dbe129d2 Mon Sep 17 00:00:00 2001 From: futengda Date: Mon, 7 Jun 2021 19:19:25 +0800 Subject: [PATCH 3/7] =?UTF-8?q?fix:=20=E8=A1=A5=E5=85=85=E4=B8=8A=E5=AF=B9?= =?UTF-8?q?=E4=BA=8E=E4=BD=8E=E7=89=88=E6=9C=ACumijs=E7=9A=84=E9=80=82?= =?UTF-8?q?=E9=85=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 如果umijs没有升级到这次提交[#6702](https://github.com/umijs/umi/pull/6702), 则使用原来的处理方式 --- packages/plugin-qiankun/src/slave/index.ts | 2 +- packages/plugin-qiankun/src/slave/lifecycles.ts.tpl | 6 ++++-- packages/plugin-qiankun/src/slave/slaveRuntimePlugin.ts.tpl | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/plugin-qiankun/src/slave/index.ts b/packages/plugin-qiankun/src/slave/index.ts index 4e72c7bbf..b67a93afa 100644 --- a/packages/plugin-qiankun/src/slave/index.ts +++ b/packages/plugin-qiankun/src/slave/index.ts @@ -146,7 +146,7 @@ export default function (api: IApi) { try { pluginArgs.appId = appId; } catch { - console.error(\`plugin-qiankun: Cannot find pluginArgs object in current scope, please check if the umijs version is greater than xxx\`); + console.error(\`[@umijs/plugin-qiankun]: Cannot find pluginArgs object in current scope, plugin-qiankun will use an insecure way to handle the clientRenderOpts, see https://github.com/umijs/plugins/pull/629 for more information. \`); } `, ); diff --git a/packages/plugin-qiankun/src/slave/lifecycles.ts.tpl b/packages/plugin-qiankun/src/slave/lifecycles.ts.tpl index f4c23de20..ac1c7a7b7 100644 --- a/packages/plugin-qiankun/src/slave/lifecycles.ts.tpl +++ b/packages/plugin-qiankun/src/slave/lifecycles.ts.tpl @@ -24,9 +24,10 @@ let hasMountedAtLeastOnce = false; export default () => defer.promise; +const unsafe_clientRenderOptsStack: any[] = []; // 对于尚未更新到这次提交(https://github.com/umijs/umi/pull/6702)的umijs版本的适配,以后应该删掉 const clientRenderOptsMap: any = {}; -export function getClientRenderOpts(appId: number) { - return { ...clientRenderOptsMap[appId] }; +export function getClientRenderOpts(appId?: number) { + return typeof appId === 'number' ? { ...clientRenderOptsMap[appId] } : unsafe_clientRenderOptsStack.pop(); } function normalizeHistory( @@ -112,6 +113,7 @@ export function genMount(mountElementId: string, appId: number) { }, }; + unsafe_clientRenderOptsStack.push(clientRenderOpts); clientRenderOptsMap[appId] = clientRenderOpts; } diff --git a/packages/plugin-qiankun/src/slave/slaveRuntimePlugin.ts.tpl b/packages/plugin-qiankun/src/slave/slaveRuntimePlugin.ts.tpl index 934c2c247..1ed8d2717 100644 --- a/packages/plugin-qiankun/src/slave/slaveRuntimePlugin.ts.tpl +++ b/packages/plugin-qiankun/src/slave/slaveRuntimePlugin.ts.tpl @@ -13,7 +13,7 @@ export const render = (oldRender: any) => { return qiankunRender().then(oldRender); }; -export function modifyClientRenderOpts(memo: any, args: any) { +export function modifyClientRenderOpts(memo: any, args: any = {}) { // 每次应用 render 的时候会调 modifyClientRenderOpts,这时根据当前的appId获取 render 的配置 const clientRenderOpts = getClientRenderOpts(args.appId); if (clientRenderOpts) { From 6f2c30fe1a34e01553e6764f1f85931339420f74 Mon Sep 17 00:00:00 2001 From: futengda Date: Mon, 7 Jun 2021 19:56:08 +0800 Subject: [PATCH 4/7] fix: delete useless `window.proxy` usage --- packages/plugin-qiankun/src/slave/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/plugin-qiankun/src/slave/index.ts b/packages/plugin-qiankun/src/slave/index.ts index b67a93afa..d864f05b1 100644 --- a/packages/plugin-qiankun/src/slave/index.ts +++ b/packages/plugin-qiankun/src/slave/index.ts @@ -142,7 +142,7 @@ export default function (api: IApi) { api.addEntryCodeAhead( () => ` - const appId = window.proxy.appId = 'appId' in window.proxy ? window.proxy.appId + 1 : 0; + const appId = window.appId = 'appId' in window? window.appId + 1 : 0; try { pluginArgs.appId = appId; } catch { From 451d366b13927e1c60527117876ee468af53b856 Mon Sep 17 00:00:00 2001 From: futengda Date: Mon, 7 Jun 2021 20:00:24 +0800 Subject: [PATCH 5/7] fix: fix typo --- packages/plugin-qiankun/src/slave/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/plugin-qiankun/src/slave/index.ts b/packages/plugin-qiankun/src/slave/index.ts index d864f05b1..0b2ae288f 100644 --- a/packages/plugin-qiankun/src/slave/index.ts +++ b/packages/plugin-qiankun/src/slave/index.ts @@ -142,7 +142,7 @@ export default function (api: IApi) { api.addEntryCodeAhead( () => ` - const appId = window.appId = 'appId' in window? window.appId + 1 : 0; + const appId = window.appId = 'appId' in window ? window.appId + 1 : 0; try { pluginArgs.appId = appId; } catch { From 2eaff9a213c6ff4edeed64f26eadcabb1bafb590 Mon Sep 17 00:00:00 2001 From: futengda Date: Tue, 8 Jun 2021 11:05:13 +0800 Subject: [PATCH 6/7] fix: delete clientRenderOpts when unmount --- packages/plugin-qiankun/src/slave/index.ts | 2 +- packages/plugin-qiankun/src/slave/lifecycles.ts.tpl | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/plugin-qiankun/src/slave/index.ts b/packages/plugin-qiankun/src/slave/index.ts index 0b2ae288f..ab9432627 100644 --- a/packages/plugin-qiankun/src/slave/index.ts +++ b/packages/plugin-qiankun/src/slave/index.ts @@ -156,7 +156,7 @@ export default function (api: IApi) { ` export const bootstrap = qiankun_genBootstrap(clientRender); export const mount = qiankun_genMount('${api.config.mountElementId}', appId); - export const unmount = qiankun_genUnmount('${api.config.mountElementId}'); + export const unmount = qiankun_genUnmount('${api.config.mountElementId}', appId); export const update = qiankun_genUpdate(); if (!window.__POWERED_BY_QIANKUN__) { diff --git a/packages/plugin-qiankun/src/slave/lifecycles.ts.tpl b/packages/plugin-qiankun/src/slave/lifecycles.ts.tpl index ac1c7a7b7..86e9a6314 100644 --- a/packages/plugin-qiankun/src/slave/lifecycles.ts.tpl +++ b/packages/plugin-qiankun/src/slave/lifecycles.ts.tpl @@ -139,7 +139,7 @@ export function genUpdate() { }; } -export function genUnmount(mountElementId: string) { +export function genUnmount(mountElementId: string, appId: number) { return async (props: any) => { const container = props?.container ? props.container.querySelector(`#${mountElementId}`) @@ -150,5 +150,7 @@ export function genUnmount(mountElementId: string) { const slaveRuntime = await getSlaveRuntime(); if (slaveRuntime.unmount) await slaveRuntime.unmount(props); + + delete clientRenderOptsMap[appId]; }; } From a106ac6ca76bb7262e064b21589f70140c9a94ce Mon Sep 17 00:00:00 2001 From: futengda Date: Tue, 8 Jun 2021 16:13:10 +0800 Subject: [PATCH 7/7] fix: `getClientRenderOpts` should return undefined when `clientRenderOptsMap[appId]` is undefined --- packages/plugin-qiankun/src/slave/lifecycles.ts.tpl | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/plugin-qiankun/src/slave/lifecycles.ts.tpl b/packages/plugin-qiankun/src/slave/lifecycles.ts.tpl index 86e9a6314..a38af0dd4 100644 --- a/packages/plugin-qiankun/src/slave/lifecycles.ts.tpl +++ b/packages/plugin-qiankun/src/slave/lifecycles.ts.tpl @@ -27,7 +27,11 @@ export default () => defer.promise; const unsafe_clientRenderOptsStack: any[] = []; // 对于尚未更新到这次提交(https://github.com/umijs/umi/pull/6702)的umijs版本的适配,以后应该删掉 const clientRenderOptsMap: any = {}; export function getClientRenderOpts(appId?: number) { - return typeof appId === 'number' ? { ...clientRenderOptsMap[appId] } : unsafe_clientRenderOptsStack.pop(); + if (typeof appId === 'number') { + return appId in clientRenderOptsMap ? { ...clientRenderOptsMap[appId] } : undefined + } else { + return unsafe_clientRenderOptsStack.pop(); + } } function normalizeHistory(