🐛 should use the new element reference while in cache scenario (#1021)
This commit is contained in:
parent
c7b0fb8c37
commit
e0cc86019b
|
|
@ -137,7 +137,7 @@ function getAppWrapperGetter(
|
||||||
const rawAppendChild = HTMLElement.prototype.appendChild;
|
const rawAppendChild = HTMLElement.prototype.appendChild;
|
||||||
const rawRemoveChild = HTMLElement.prototype.removeChild;
|
const rawRemoveChild = HTMLElement.prototype.removeChild;
|
||||||
type ElementRender = (
|
type ElementRender = (
|
||||||
props: { element: HTMLElement | null; loading: boolean; remountContainer?: string | HTMLElement },
|
props: { element: HTMLElement | null; loading: boolean; container?: string | HTMLElement },
|
||||||
phase: 'loading' | 'mounting' | 'mounted' | 'unmounted',
|
phase: 'loading' | 'mounting' | 'mounted' | 'unmounted',
|
||||||
) => any;
|
) => any;
|
||||||
|
|
||||||
|
|
@ -146,16 +146,10 @@ type ElementRender = (
|
||||||
* If the legacy render function is provide, used as it, otherwise we will insert the app element to target container by qiankun
|
* If the legacy render function is provide, used as it, otherwise we will insert the app element to target container by qiankun
|
||||||
* @param appName
|
* @param appName
|
||||||
* @param appContent
|
* @param appContent
|
||||||
* @param container
|
|
||||||
* @param legacyRender
|
* @param legacyRender
|
||||||
*/
|
*/
|
||||||
function getRender(
|
function getRender(appName: string, appContent: string, legacyRender?: HTMLContentRender) {
|
||||||
appName: string,
|
const render: ElementRender = ({ element, loading, container }, phase) => {
|
||||||
appContent: string,
|
|
||||||
container?: string | HTMLElement,
|
|
||||||
legacyRender?: HTMLContentRender,
|
|
||||||
) {
|
|
||||||
const render: ElementRender = ({ element, loading, remountContainer }, phase) => {
|
|
||||||
if (legacyRender) {
|
if (legacyRender) {
|
||||||
if (process.env.NODE_ENV === 'development') {
|
if (process.env.NODE_ENV === 'development') {
|
||||||
console.warn(
|
console.warn(
|
||||||
|
|
@ -166,7 +160,7 @@ function getRender(
|
||||||
return legacyRender({ loading, appContent: element ? appContent : '' });
|
return legacyRender({ loading, appContent: element ? appContent : '' });
|
||||||
}
|
}
|
||||||
|
|
||||||
const containerElement = getContainer(remountContainer || container!);
|
const containerElement = getContainer(container!);
|
||||||
|
|
||||||
// The container might have be removed after micro app unmounted.
|
// The container might have be removed after micro app unmounted.
|
||||||
// Such as the micro app unmount lifecycle called by a react componentWillUnmount lifecycle, after micro app unmounted, the react component might also be removed
|
// Such as the micro app unmount lifecycle called by a react componentWillUnmount lifecycle, after micro app unmounted, the react component might also be removed
|
||||||
|
|
@ -258,24 +252,29 @@ export async function loadApp<T extends object>(
|
||||||
|
|
||||||
const strictStyleIsolation = typeof sandbox === 'object' && !!sandbox.strictStyleIsolation;
|
const strictStyleIsolation = typeof sandbox === 'object' && !!sandbox.strictStyleIsolation;
|
||||||
const scopedCSS = isEnableScopedCSS(sandbox);
|
const scopedCSS = isEnableScopedCSS(sandbox);
|
||||||
let appWrapperElement: HTMLElement | null = createElement(appContent, strictStyleIsolation, scopedCSS, appName);
|
let initialAppWrapperElement: HTMLElement | null = createElement(
|
||||||
|
appContent,
|
||||||
|
strictStyleIsolation,
|
||||||
|
scopedCSS,
|
||||||
|
appName,
|
||||||
|
);
|
||||||
|
|
||||||
const container = 'container' in app ? app.container : undefined;
|
const initialContainer = 'container' in app ? app.container : undefined;
|
||||||
const legacyRender = 'render' in app ? app.render : undefined;
|
const legacyRender = 'render' in app ? app.render : undefined;
|
||||||
|
|
||||||
const render = getRender(appName, appContent, container, legacyRender);
|
const render = getRender(appName, appContent, legacyRender);
|
||||||
|
|
||||||
// 第一次加载设置应用可见区域 dom 结构
|
// 第一次加载设置应用可见区域 dom 结构
|
||||||
// 确保每次应用加载前容器 dom 结构已经设置完毕
|
// 确保每次应用加载前容器 dom 结构已经设置完毕
|
||||||
render({ element: appWrapperElement, loading: true }, 'loading');
|
render({ element: initialAppWrapperElement, loading: true, container: initialContainer }, 'loading');
|
||||||
|
|
||||||
const appWrapperGetter = getAppWrapperGetter(
|
const initialAppWrapperGetter = getAppWrapperGetter(
|
||||||
appName,
|
appName,
|
||||||
appInstanceId,
|
appInstanceId,
|
||||||
!!legacyRender,
|
!!legacyRender,
|
||||||
strictStyleIsolation,
|
strictStyleIsolation,
|
||||||
scopedCSS,
|
scopedCSS,
|
||||||
() => appWrapperElement,
|
() => initialAppWrapperElement,
|
||||||
);
|
);
|
||||||
|
|
||||||
let global = window;
|
let global = window;
|
||||||
|
|
@ -283,7 +282,14 @@ export async function loadApp<T extends object>(
|
||||||
let unmountSandbox = () => Promise.resolve();
|
let unmountSandbox = () => Promise.resolve();
|
||||||
const useLooseSandbox = typeof sandbox === 'object' && !!sandbox.loose;
|
const useLooseSandbox = typeof sandbox === 'object' && !!sandbox.loose;
|
||||||
if (sandbox) {
|
if (sandbox) {
|
||||||
const sandboxInstance = createSandbox(appName, appWrapperGetter, scopedCSS, useLooseSandbox, excludeAssetFilter);
|
const sandboxInstance = createSandbox(
|
||||||
|
appName,
|
||||||
|
// FIXME should use a strict sandbox logic while remount, see https://github.com/umijs/qiankun/issues/518
|
||||||
|
initialAppWrapperGetter,
|
||||||
|
scopedCSS,
|
||||||
|
useLooseSandbox,
|
||||||
|
excludeAssetFilter,
|
||||||
|
);
|
||||||
// 用沙箱的代理对象作为接下来使用的全局对象
|
// 用沙箱的代理对象作为接下来使用的全局对象
|
||||||
global = sandboxInstance.proxy as typeof window;
|
global = sandboxInstance.proxy as typeof window;
|
||||||
mountSandbox = sandboxInstance.mount;
|
mountSandbox = sandboxInstance.mount;
|
||||||
|
|
@ -309,7 +315,20 @@ export async function loadApp<T extends object>(
|
||||||
offGlobalStateChange,
|
offGlobalStateChange,
|
||||||
}: Record<string, Function> = getMicroAppStateActions(appInstanceId);
|
}: Record<string, Function> = getMicroAppStateActions(appInstanceId);
|
||||||
|
|
||||||
const parcelConfigGetter: ParcelConfigObjectGetter = (remountContainer) => {
|
// FIXME temporary way
|
||||||
|
const syncAppWrapperElement2Sandbox = (element: HTMLElement | null) => (initialAppWrapperElement = element);
|
||||||
|
|
||||||
|
const parcelConfigGetter: ParcelConfigObjectGetter = (remountContainer = initialContainer) => {
|
||||||
|
let appWrapperElement: HTMLElement | null = initialAppWrapperElement;
|
||||||
|
const appWrapperGetter = getAppWrapperGetter(
|
||||||
|
appName,
|
||||||
|
appInstanceId,
|
||||||
|
!!legacyRender,
|
||||||
|
strictStyleIsolation,
|
||||||
|
scopedCSS,
|
||||||
|
() => appWrapperElement,
|
||||||
|
);
|
||||||
|
|
||||||
const parcelConfig: ParcelConfigObject = {
|
const parcelConfig: ParcelConfigObject = {
|
||||||
name: appInstanceId,
|
name: appInstanceId,
|
||||||
bootstrap,
|
bootstrap,
|
||||||
|
|
@ -332,16 +351,22 @@ export async function loadApp<T extends object>(
|
||||||
},
|
},
|
||||||
// 添加 mount hook, 确保每次应用加载前容器 dom 结构已经设置完毕
|
// 添加 mount hook, 确保每次应用加载前容器 dom 结构已经设置完毕
|
||||||
async () => {
|
async () => {
|
||||||
// element would be destroyed after unmounted, we need to recreate it if it not exist
|
const useNewContainer = remountContainer !== initialContainer;
|
||||||
appWrapperElement = appWrapperElement || createElement(appContent, strictStyleIsolation, scopedCSS, appName);
|
if (useNewContainer || !appWrapperElement) {
|
||||||
render({ element: appWrapperElement, loading: true, remountContainer }, 'mounting');
|
// element will be destroyed after unmounted, we need to recreate it if it not exist
|
||||||
|
// or we try to remount into a new container
|
||||||
|
appWrapperElement = createElement(appContent, strictStyleIsolation, scopedCSS, appName);
|
||||||
|
syncAppWrapperElement2Sandbox(appWrapperElement);
|
||||||
|
}
|
||||||
|
|
||||||
|
render({ element: appWrapperElement, loading: true, container: remountContainer }, 'mounting');
|
||||||
},
|
},
|
||||||
mountSandbox,
|
mountSandbox,
|
||||||
// exec the chain after rendering to keep the behavior with beforeLoad
|
// exec the chain after rendering to keep the behavior with beforeLoad
|
||||||
async () => execHooksChain(toArray(beforeMount), app, global),
|
async () => execHooksChain(toArray(beforeMount), app, global),
|
||||||
async (props) => mount({ ...props, container: appWrapperGetter(), setGlobalState, onGlobalStateChange }),
|
async (props) => mount({ ...props, container: appWrapperGetter(), setGlobalState, onGlobalStateChange }),
|
||||||
// finish loading after app mounted
|
// finish loading after app mounted
|
||||||
async () => render({ element: appWrapperElement, loading: false, remountContainer }, 'mounted'),
|
async () => render({ element: appWrapperElement, loading: false, container: remountContainer }, 'mounted'),
|
||||||
async () => execHooksChain(toArray(afterMount), app, global),
|
async () => execHooksChain(toArray(afterMount), app, global),
|
||||||
// initialize the unmount defer after app mounted and resolve the defer after it unmounted
|
// initialize the unmount defer after app mounted and resolve the defer after it unmounted
|
||||||
async () => {
|
async () => {
|
||||||
|
|
@ -362,10 +387,11 @@ export async function loadApp<T extends object>(
|
||||||
unmountSandbox,
|
unmountSandbox,
|
||||||
async () => execHooksChain(toArray(afterUnmount), app, global),
|
async () => execHooksChain(toArray(afterUnmount), app, global),
|
||||||
async () => {
|
async () => {
|
||||||
render({ element: null, loading: false, remountContainer }, 'unmounted');
|
render({ element: null, loading: false, container: remountContainer }, 'unmounted');
|
||||||
offGlobalStateChange(appInstanceId);
|
offGlobalStateChange(appInstanceId);
|
||||||
// for gc
|
// for gc
|
||||||
appWrapperElement = null;
|
appWrapperElement = null;
|
||||||
|
syncAppWrapperElement2Sandbox(appWrapperElement);
|
||||||
},
|
},
|
||||||
async () => {
|
async () => {
|
||||||
if ((await validateSingularMode(singular, app)) && prevAppUnmountedDeferred) {
|
if ((await validateSingularMode(singular, app)) && prevAppUnmountedDeferred) {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user