diff --git a/examples/main/index.js b/examples/main/index.js index 8dcfe9dcf..d0c54580f 100644 --- a/examples/main/index.js +++ b/examples/main/index.js @@ -1,12 +1,12 @@ import 'zone.js'; // for angular subapp -import { registerMicroApps, runAfterFirstMounted, setDefaultMountApp, start, initGlobalState } from '../../es'; +import { initGlobalState, registerMicroApps, runAfterFirstMounted, setDefaultMountApp, start } from '../../es'; import './index.less'; - /** * 主应用 **可以使用任意技术栈** * 以下分别是 React 和 Vue 的示例,可切换尝试 */ import render from './render/ReactRender'; + // import render from './render/VueRender'; /** @@ -14,7 +14,7 @@ import render from './render/ReactRender'; */ render({ loading: true }); -const loader = loading => render({ loading }); +const loader = (loading) => render({ loading }); /** * Step2 注册子应用 @@ -67,17 +67,17 @@ registerMicroApps( ], { beforeLoad: [ - app => { + (app) => { console.log('[LifeCycle] before load %c%s', 'color: green;', app.name); }, ], beforeMount: [ - app => { + (app) => { console.log('[LifeCycle] before mount %c%s', 'color: green;', app.name); }, ], afterUnmount: [ - app => { + (app) => { console.log('[LifeCycle] after unmount %c%s', 'color: green;', app.name); }, ], diff --git a/examples/react15/index.js b/examples/react15/index.js index 54d2dbf5e..750416ebc 100644 --- a/examples/react15/index.js +++ b/examples/react15/index.js @@ -2,13 +2,12 @@ * @author Kuitos * @since 2019-05-16 */ -import './public-path'; +import 'antd/dist/antd.min.css'; import React from 'react'; import ReactDOM from 'react-dom'; import App from './App'; - -import 'antd/dist/antd.min.css'; import './index.css'; +import './public-path'; export async function bootstrap() { console.log('[react15] react app bootstraped'); @@ -24,6 +23,14 @@ export async function mount(props = {}) { import('./dynamic.css').then(() => { console.log('[react15] dynamic style load'); }); + + const styleElement = document.createElement('style'); + styleElement.innerText = '.react15-icon { height: 400px }'; + document.head.appendChild(styleElement); + + setTimeout(() => { + document.head.removeChild(styleElement); + }, 2000); } export async function unmount(props) { diff --git a/src/__tests__/utils.test.ts b/src/__tests__/utils.test.ts index 2fb1683d8..6eb047779 100644 --- a/src/__tests__/utils.test.ts +++ b/src/__tests__/utils.test.ts @@ -29,7 +29,8 @@ test('should wrap string with div', () => { const ret = factory(tpl); expect(ret).toBe( - `
${tpl}
`, + // eslint-disable-next-line max-len + `
${tpl}
`, ); }); diff --git a/src/sandbox/patchers/dynamicAppend/common.ts b/src/sandbox/patchers/dynamicAppend/common.ts index 3e62daf29..c301e83c6 100644 --- a/src/sandbox/patchers/dynamicAppend/common.ts +++ b/src/sandbox/patchers/dynamicAppend/common.ts @@ -21,19 +21,19 @@ const STYLE_TAG_NAME = 'STYLE'; export const styleElementTargetSymbol = Symbol('target'); -type DynamicAppendTarget = 'head' | 'body'; +type DynamicDomMutationTarget = 'head' | 'body'; declare global { interface HTMLLinkElement { - [styleElementTargetSymbol]: DynamicAppendTarget; + [styleElementTargetSymbol]: DynamicDomMutationTarget; } interface HTMLStyleElement { - [styleElementTargetSymbol]: DynamicAppendTarget; + [styleElementTargetSymbol]: DynamicDomMutationTarget; } } -export const getAppWrapperHeadElement = (appWrapper: Element | ShadowRoot) => { +export const getAppWrapperHeadElement = (appWrapper: Element | ShadowRoot): Element => { const rootElement = 'host' in appWrapper ? appWrapper.host : appWrapper; return rootElement.getElementsByTagName(qiankunHeadTagName)[0]; }; @@ -158,7 +158,7 @@ export type ContainerConfig = { appName: string; proxy: WindowProxy; strictGlobal: boolean; - dynamicStyleSheetElements: HTMLStyleElement[]; + dynamicStyleSheetElements: Array; appWrapperGetter: CallableFunction; scopedCSS: boolean; excludeAssetFilter?: CallableFunction; @@ -168,7 +168,7 @@ function getOverwrittenAppendChildOrInsertBefore(opts: { rawDOMAppendOrInsertBefore: (newChild: T, refChild?: Node | null) => T; isInvokedByMicroApp: (element: HTMLElement) => boolean; containerConfigGetter: (element: HTMLElement) => ContainerConfig; - target: DynamicAppendTarget; + target: DynamicDomMutationTarget; }) { return function appendChildOrInsertBefore( this: HTMLHeadElement | HTMLBodyElement, @@ -209,7 +209,6 @@ function getOverwrittenAppendChildOrInsertBefore(opts: { }); const appWrapper = appWrapperGetter(); - const mountDOM = target === 'head' ? getAppWrapperHeadElement(appWrapper) : appWrapper; if (scopedCSS) { // exclude link elements like @@ -224,16 +223,17 @@ function getOverwrittenAppendChildOrInsertBefore(opts: { : frameworkConfiguration.fetch?.fn; stylesheetElement = convertLinkAsStyle( element, - (styleElement) => css.process(mountDOM, styleElement, appName), + (styleElement) => css.process(appWrapper, styleElement, appName), fetch, ); dynamicLinkAttachedInlineStyleMap.set(element, stylesheetElement); } else { - css.process(mountDOM, stylesheetElement, appName); + css.process(appWrapper, stylesheetElement, appName); } } - // eslint-disable-next-line no-shadow + const mountDOM = target === 'head' ? getAppWrapperHeadElement(appWrapper) : appWrapper; + dynamicStyleSheetElements.push(stylesheetElement); const referenceNode = mountDOM.contains(refChild) ? refChild : null; return rawDOMAppendOrInsertBefore.call(mountDOM, stylesheetElement, referenceNode); @@ -301,7 +301,8 @@ function getOverwrittenAppendChildOrInsertBefore(opts: { function getNewRemoveChild( headOrBodyRemoveChild: typeof HTMLElement.prototype.removeChild, - appWrapperGetterGetter: (element: HTMLElement) => ContainerConfig['appWrapperGetter'], + containerConfigGetter: (element: HTMLElement) => ContainerConfig, + target: DynamicDomMutationTarget, ) { return function removeChild(this: HTMLHeadElement | HTMLBodyElement, child: T) { const { tagName } = child as any; @@ -309,14 +310,19 @@ function getNewRemoveChild( try { let attachedElement: Node; + const { appWrapperGetter, dynamicStyleSheetElements } = containerConfigGetter(child as any); + switch (tagName) { + case STYLE_TAG_NAME: case LINK_TAG_NAME: { - attachedElement = (dynamicLinkAttachedInlineStyleMap.get(child as any) as Node) || child; + attachedElement = dynamicLinkAttachedInlineStyleMap.get(child as any) || child; + // try to remove the dynamic style sheet + dynamicStyleSheetElements.splice(dynamicStyleSheetElements.indexOf(attachedElement as any), 1); break; } case SCRIPT_TAG_NAME: { - attachedElement = (dynamicScriptAttachedCommentMap.get(child as any) as Node) || child; + attachedElement = dynamicScriptAttachedCommentMap.get(child as any) || child; break; } @@ -325,11 +331,11 @@ function getNewRemoveChild( } } - // container may had been removed while app unmounting if the removeChild action was async - const appWrapperGetter = appWrapperGetterGetter(child as any); - const container = appWrapperGetter(); + const appWrapper = appWrapperGetter(); + const container = target === 'head' ? getAppWrapperHeadElement(appWrapper) : appWrapper; + // container might have been removed while app unmounting if the removeChild action was async if (container.contains(attachedElement)) { - return rawRemoveChild.call(container, attachedElement) as T; + return rawRemoveChild.call(attachedElement.parentNode, attachedElement) as T; } } catch (e) { console.warn(e); @@ -375,14 +381,8 @@ export function patchHTMLDynamicAppendPrototypeFunctions( HTMLHeadElement.prototype.removeChild === rawHeadRemoveChild && HTMLBodyElement.prototype.removeChild === rawBodyRemoveChild ) { - HTMLHeadElement.prototype.removeChild = getNewRemoveChild( - rawHeadRemoveChild, - (element) => containerConfigGetter(element).appWrapperGetter, - ); - HTMLBodyElement.prototype.removeChild = getNewRemoveChild( - rawBodyRemoveChild, - (element) => containerConfigGetter(element).appWrapperGetter, - ); + HTMLHeadElement.prototype.removeChild = getNewRemoveChild(rawHeadRemoveChild, containerConfigGetter, 'head'); + HTMLBodyElement.prototype.removeChild = getNewRemoveChild(rawBodyRemoveChild, containerConfigGetter, 'body'); } return function unpatch() { diff --git a/src/sandbox/patchers/dynamicAppend/forStrictSandbox.ts b/src/sandbox/patchers/dynamicAppend/forStrictSandbox.ts index c301b4e6c..7f003143a 100644 --- a/src/sandbox/patchers/dynamicAppend/forStrictSandbox.ts +++ b/src/sandbox/patchers/dynamicAppend/forStrictSandbox.ts @@ -117,7 +117,7 @@ export function patchStrictSandbox( if (mounting) mountingPatchCount--; const allMicroAppUnmounted = mountingPatchCount === 0 && bootstrappingPatchCount === 0; - // release the overwrite prototype after all the micro apps unmounted + // release the overwritten prototype after all the micro apps unmounted if (allMicroAppUnmounted) { unpatchDynamicAppendPrototypeFunctions(); unpatchDocumentCreate(); diff --git a/src/utils.ts b/src/utils.ts index 18cc1d122..c81e72358 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -107,10 +107,18 @@ export const qiankunHeadTagName = 'qiankun-head'; export function getDefaultTplWrapper(name: string) { return (tpl: string) => { - // We need to mock a head placeholder as native head element will be erased by browser in micro app - const tplWithSimulatedHead = tpl - .replace('', `<${qiankunHeadTagName}>`) - .replace('', ``); + let tplWithSimulatedHead: string; + + if (tpl.indexOf('') !== -1) { + // We need to mock a head placeholder as native head element will be erased by browser in micro app + tplWithSimulatedHead = tpl + .replace('', `<${qiankunHeadTagName}>`) + .replace('', ``); + } else { + // Some template might not be a standard html document, thus we need to add a simulated head tag for them + tplWithSimulatedHead = `<${qiankunHeadTagName}>${tpl}`; + } + return `
${tplWithSimulatedHead}
`; diff --git a/src/version.ts b/src/version.ts index a7250834f..edf45a6b4 100644 --- a/src/version.ts +++ b/src/version.ts @@ -1 +1 @@ -export { version } from '../package.json'; +export const version = '2.7.1';