🐛 compatible with overwritten appendChild by apps themselves (#2449)
This commit is contained in:
parent
601696ad7f
commit
6e5b145046
|
|
@ -9,18 +9,12 @@ import { qiankunHeadTagName } from '../../../utils';
|
||||||
import { cachedGlobals } from '../../proxySandbox';
|
import { cachedGlobals } from '../../proxySandbox';
|
||||||
import * as css from '../css';
|
import * as css from '../css';
|
||||||
|
|
||||||
export const rawHeadAppendChild = HTMLHeadElement.prototype.appendChild;
|
|
||||||
const rawHeadRemoveChild = HTMLHeadElement.prototype.removeChild;
|
|
||||||
const rawBodyAppendChild = HTMLBodyElement.prototype.appendChild;
|
|
||||||
const rawBodyRemoveChild = HTMLBodyElement.prototype.removeChild;
|
|
||||||
const rawHeadInsertBefore = HTMLHeadElement.prototype.insertBefore;
|
|
||||||
const rawRemoveChild = HTMLElement.prototype.removeChild;
|
|
||||||
|
|
||||||
const SCRIPT_TAG_NAME = 'SCRIPT';
|
const SCRIPT_TAG_NAME = 'SCRIPT';
|
||||||
const LINK_TAG_NAME = 'LINK';
|
const LINK_TAG_NAME = 'LINK';
|
||||||
const STYLE_TAG_NAME = 'STYLE';
|
const STYLE_TAG_NAME = 'STYLE';
|
||||||
|
|
||||||
export const styleElementTargetSymbol = Symbol('target');
|
export const styleElementTargetSymbol = Symbol('target');
|
||||||
|
const overwrittenSymbol = Symbol('qiankun-overwritten');
|
||||||
|
|
||||||
type DynamicDomMutationTarget = 'head' | 'body';
|
type DynamicDomMutationTarget = 'head' | 'body';
|
||||||
|
|
||||||
|
|
@ -32,6 +26,10 @@ declare global {
|
||||||
interface HTMLStyleElement {
|
interface HTMLStyleElement {
|
||||||
[styleElementTargetSymbol]: DynamicDomMutationTarget;
|
[styleElementTargetSymbol]: DynamicDomMutationTarget;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface Function {
|
||||||
|
[overwrittenSymbol]: boolean;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getAppWrapperHeadElement = (appWrapper: Element | ShadowRoot): Element => {
|
export const getAppWrapperHeadElement = (appWrapper: Element | ShadowRoot): Element => {
|
||||||
|
|
@ -69,6 +67,7 @@ export function isStyledComponentsLike(element: HTMLStyleElement) {
|
||||||
}
|
}
|
||||||
|
|
||||||
const appsCounterMap = new Map<string, { bootstrappingPatchCount: number; mountingPatchCount: number }>();
|
const appsCounterMap = new Map<string, { bootstrappingPatchCount: number; mountingPatchCount: number }>();
|
||||||
|
|
||||||
export function calcAppCount(
|
export function calcAppCount(
|
||||||
appName: string,
|
appName: string,
|
||||||
calcType: 'increase' | 'decrease',
|
calcType: 'increase' | 'decrease',
|
||||||
|
|
@ -88,6 +87,7 @@ export function calcAppCount(
|
||||||
}
|
}
|
||||||
appsCounterMap.set(appName, appCount);
|
appsCounterMap.set(appName, appCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isAllAppsUnmounted(): boolean {
|
export function isAllAppsUnmounted(): boolean {
|
||||||
return Array.from(appsCounterMap.entries()).every(
|
return Array.from(appsCounterMap.entries()).every(
|
||||||
([, { bootstrappingPatchCount: bpc, mountingPatchCount: mpc }]) => bpc === 0 && mpc === 0,
|
([, { bootstrappingPatchCount: bpc, mountingPatchCount: mpc }]) => bpc === 0 && mpc === 0,
|
||||||
|
|
@ -197,7 +197,7 @@ function getOverwrittenAppendChildOrInsertBefore(opts: {
|
||||||
containerConfigGetter: (element: HTMLElement) => ContainerConfig;
|
containerConfigGetter: (element: HTMLElement) => ContainerConfig;
|
||||||
target: DynamicDomMutationTarget;
|
target: DynamicDomMutationTarget;
|
||||||
}) {
|
}) {
|
||||||
return function appendChildOrInsertBefore<T extends Node>(
|
function appendChildOrInsertBefore<T extends Node>(
|
||||||
this: HTMLHeadElement | HTMLBodyElement,
|
this: HTMLHeadElement | HTMLBodyElement,
|
||||||
newChild: T,
|
newChild: T,
|
||||||
refChild: Node | null = null,
|
refChild: Node | null = null,
|
||||||
|
|
@ -339,19 +339,22 @@ function getOverwrittenAppendChildOrInsertBefore(opts: {
|
||||||
}
|
}
|
||||||
|
|
||||||
return rawDOMAppendOrInsertBefore.call(this, element, refChild);
|
return rawDOMAppendOrInsertBefore.call(this, element, refChild);
|
||||||
};
|
}
|
||||||
|
|
||||||
|
appendChildOrInsertBefore[overwrittenSymbol] = true;
|
||||||
|
|
||||||
|
return appendChildOrInsertBefore;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getNewRemoveChild(
|
function getNewRemoveChild(
|
||||||
headOrBodyRemoveChild: typeof HTMLElement.prototype.removeChild,
|
rawRemoveChild: typeof HTMLElement.prototype.removeChild,
|
||||||
containerConfigGetter: (element: HTMLElement) => ContainerConfig,
|
containerConfigGetter: (element: HTMLElement) => ContainerConfig,
|
||||||
target: DynamicDomMutationTarget,
|
target: DynamicDomMutationTarget,
|
||||||
isInvokedByMicroApp: (element: HTMLElement) => boolean,
|
isInvokedByMicroApp: (element: HTMLElement) => boolean,
|
||||||
) {
|
) {
|
||||||
return function removeChild<T extends Node>(this: HTMLHeadElement | HTMLBodyElement, child: T) {
|
function removeChild<T extends Node>(this: HTMLHeadElement | HTMLBodyElement, child: T) {
|
||||||
const { tagName } = child as any;
|
const { tagName } = child as any;
|
||||||
if (!isHijackingTag(tagName) || !isInvokedByMicroApp(child as any))
|
if (!isHijackingTag(tagName) || !isInvokedByMicroApp(child as any)) return rawRemoveChild.call(this, child) as T;
|
||||||
return headOrBodyRemoveChild.call(this, child) as T;
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
let attachedElement: Node;
|
let attachedElement: Node;
|
||||||
|
|
@ -391,19 +394,26 @@ function getNewRemoveChild(
|
||||||
console.warn(e);
|
console.warn(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
return headOrBodyRemoveChild.call(this, child) as T;
|
return rawRemoveChild.call(this, child) as T;
|
||||||
};
|
}
|
||||||
|
|
||||||
|
removeChild[overwrittenSymbol] = true;
|
||||||
|
return removeChild;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function patchHTMLDynamicAppendPrototypeFunctions(
|
export function patchHTMLDynamicAppendPrototypeFunctions(
|
||||||
isInvokedByMicroApp: (element: HTMLElement) => boolean,
|
isInvokedByMicroApp: (element: HTMLElement) => boolean,
|
||||||
containerConfigGetter: (element: HTMLElement) => ContainerConfig,
|
containerConfigGetter: (element: HTMLElement) => ContainerConfig,
|
||||||
) {
|
) {
|
||||||
|
const rawHeadAppendChild = HTMLHeadElement.prototype.appendChild;
|
||||||
|
const rawBodyAppendChild = HTMLBodyElement.prototype.appendChild;
|
||||||
|
const rawHeadInsertBefore = HTMLHeadElement.prototype.insertBefore;
|
||||||
|
|
||||||
// Just overwrite it while it have not been overwritten
|
// Just overwrite it while it have not been overwritten
|
||||||
if (
|
if (
|
||||||
HTMLHeadElement.prototype.appendChild === rawHeadAppendChild &&
|
rawHeadAppendChild[overwrittenSymbol] !== true &&
|
||||||
HTMLBodyElement.prototype.appendChild === rawBodyAppendChild &&
|
rawBodyAppendChild[overwrittenSymbol] !== true &&
|
||||||
HTMLHeadElement.prototype.insertBefore === rawHeadInsertBefore
|
rawHeadInsertBefore[overwrittenSymbol] !== true
|
||||||
) {
|
) {
|
||||||
HTMLHeadElement.prototype.appendChild = getOverwrittenAppendChildOrInsertBefore({
|
HTMLHeadElement.prototype.appendChild = getOverwrittenAppendChildOrInsertBefore({
|
||||||
rawDOMAppendOrInsertBefore: rawHeadAppendChild,
|
rawDOMAppendOrInsertBefore: rawHeadAppendChild,
|
||||||
|
|
@ -426,11 +436,10 @@ export function patchHTMLDynamicAppendPrototypeFunctions(
|
||||||
}) as typeof rawHeadInsertBefore;
|
}) as typeof rawHeadInsertBefore;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const rawHeadRemoveChild = HTMLHeadElement.prototype.removeChild;
|
||||||
|
const rawBodyRemoveChild = HTMLBodyElement.prototype.removeChild;
|
||||||
// Just overwrite it while it have not been overwritten
|
// Just overwrite it while it have not been overwritten
|
||||||
if (
|
if (rawHeadRemoveChild[overwrittenSymbol] !== true && rawBodyRemoveChild[overwrittenSymbol] !== true) {
|
||||||
HTMLHeadElement.prototype.removeChild === rawHeadRemoveChild &&
|
|
||||||
HTMLBodyElement.prototype.removeChild === rawBodyRemoveChild
|
|
||||||
) {
|
|
||||||
HTMLHeadElement.prototype.removeChild = getNewRemoveChild(
|
HTMLHeadElement.prototype.removeChild = getNewRemoveChild(
|
||||||
rawHeadRemoveChild,
|
rawHeadRemoveChild,
|
||||||
containerConfigGetter,
|
containerConfigGetter,
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,6 @@ import {
|
||||||
isAllAppsUnmounted,
|
isAllAppsUnmounted,
|
||||||
isHijackingTag,
|
isHijackingTag,
|
||||||
patchHTMLDynamicAppendPrototypeFunctions,
|
patchHTMLDynamicAppendPrototypeFunctions,
|
||||||
rawHeadAppendChild,
|
|
||||||
rebuildCSSRules,
|
rebuildCSSRules,
|
||||||
recordStyledComponentsCSSRules,
|
recordStyledComponentsCSSRules,
|
||||||
styleElementTargetSymbol,
|
styleElementTargetSymbol,
|
||||||
|
|
@ -22,6 +21,8 @@ import {
|
||||||
// Get native global window with a sandbox disgusted way, thus we could share it between qiankun instances🤪
|
// Get native global window with a sandbox disgusted way, thus we could share it between qiankun instances🤪
|
||||||
Object.defineProperty(nativeGlobal, '__proxyAttachContainerConfigMap__', { enumerable: false, writable: true });
|
Object.defineProperty(nativeGlobal, '__proxyAttachContainerConfigMap__', { enumerable: false, writable: true });
|
||||||
|
|
||||||
|
const rawHeadAppendChild = HTMLHeadElement.prototype.appendChild;
|
||||||
|
|
||||||
// Share proxyAttachContainerConfigMap between multiple qiankun instance, thus they could access the same record
|
// Share proxyAttachContainerConfigMap between multiple qiankun instance, thus they could access the same record
|
||||||
nativeGlobal.__proxyAttachContainerConfigMap__ =
|
nativeGlobal.__proxyAttachContainerConfigMap__ =
|
||||||
nativeGlobal.__proxyAttachContainerConfigMap__ || new WeakMap<WindowProxy, ContainerConfig>();
|
nativeGlobal.__proxyAttachContainerConfigMap__ || new WeakMap<WindowProxy, ContainerConfig>();
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user