From 66c94a66f3c7377cd55e05fd756608883aff50c6 Mon Sep 17 00:00:00 2001 From: Kuitos Date: Mon, 19 Oct 2020 18:55:23 +0800 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20experimentalStyleIsolation=20suppor?= =?UTF-8?q?t=20dynamic=20link=20style=20appending=20(#1005)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * ✨ experimentalStyleIsolation support dynamic link style appending * 🎨 rename function name --- src/sandbox/patchers/css.ts | 6 +- src/sandbox/patchers/dynamicAppend/common.ts | 68 ++++++++++++++++---- 2 files changed, 61 insertions(+), 13 deletions(-) diff --git a/src/sandbox/patchers/css.ts b/src/sandbox/patchers/css.ts index daa5637..f4991bc 100644 --- a/src/sandbox/patchers/css.ts +++ b/src/sandbox/patchers/css.ts @@ -22,6 +22,8 @@ const arrayify = (list: CSSRuleList | any[]) => { return [].slice.call(list, 0) as T[]; }; +const rawDocumentBodyAppend = document.body.appendChild; + export class ScopedCSS { private static ModifiedTag = 'Symbol(style-modified-qiankun)'; @@ -31,7 +33,7 @@ export class ScopedCSS { constructor() { const styleNode = document.createElement('style'); - document.body.appendChild(styleNode); + rawDocumentBodyAppend.call(document.body, styleNode); this.swapNode = styleNode; this.sheet = styleNode.sheet!; @@ -181,7 +183,7 @@ export const process = ( appWrapper: HTMLElement, stylesheetElement: HTMLStyleElement | HTMLLinkElement, appName: string, -) => { +): void => { // lazy singleton pattern if (!processor) { processor = new ScopedCSS(); diff --git a/src/sandbox/patchers/dynamicAppend/common.ts b/src/sandbox/patchers/dynamicAppend/common.ts index 664b54f..7491c36 100644 --- a/src/sandbox/patchers/dynamicAppend/common.ts +++ b/src/sandbox/patchers/dynamicAppend/common.ts @@ -40,8 +40,29 @@ export function isStyledComponentsLike(element: HTMLStyleElement) { ); } +function convertLinkAsStyle( + element: HTMLLinkElement, + postProcess: (styleElement: HTMLStyleElement) => void, + fetchFn = fetch, +): HTMLStyleElement { + const styleElement = document.createElement('style'); + const { href } = element; + // add source link element href + styleElement.dataset.qiankunHref = href; + + fetchFn(href) + .then((res: any) => res.text()) + .then((styleContext: string) => { + styleElement.appendChild(document.createTextNode(styleContext)); + postProcess(styleElement); + }); + + return styleElement; +} + const styledComponentCSSRulesMap = new WeakMap(); const dynamicScriptAttachedCommentMap = new WeakMap(); +const dynamicLinkAttachedInlineStyleMap = new WeakMap(); export function recordStyledComponentsCSSRules(styleElements: HTMLStyleElement[]): void { styleElements.forEach((styleElement) => { @@ -116,7 +137,7 @@ function getOverwrittenAppendChildOrInsertBefore(opts: { switch (element.tagName) { case LINK_TAG_NAME: case STYLE_TAG_NAME: { - const stylesheetElement: HTMLLinkElement | HTMLStyleElement = newChild as any; + let stylesheetElement: HTMLLinkElement | HTMLStyleElement = newChild as any; const { href } = stylesheetElement as HTMLLinkElement; if (excludeAssetFilter && href && excludeAssetFilter(href)) { return rawDOMAppendOrInsertBefore.call(this, element, refChild) as T; @@ -125,7 +146,17 @@ function getOverwrittenAppendChildOrInsertBefore(opts: { const mountDOM = appWrapperGetter(); if (scopedCSS) { - css.process(mountDOM, stylesheetElement, appName); + if (element.tagName === LINK_TAG_NAME) { + const { fetch } = frameworkConfiguration; + stylesheetElement = convertLinkAsStyle( + element, + (styleElement) => css.process(mountDOM, styleElement, appName), + fetch, + ); + dynamicLinkAttachedInlineStyleMap.set(element, stylesheetElement); + } else { + css.process(mountDOM, stylesheetElement, appName); + } } // eslint-disable-next-line no-shadow @@ -212,17 +243,32 @@ function getNewRemoveChild( appWrapperGetterGetter: (element: HTMLElement) => ContainerConfig['appWrapperGetter'], ) { return function removeChild(this: HTMLHeadElement | HTMLBodyElement, child: T) { - try { - const { tagName } = child as any; - if (isHijackingTag(tagName)) { - const appWrapperGetter = appWrapperGetterGetter(child as any); + const { tagName } = child as any; + if (!isHijackingTag(tagName)) return headOrBodyRemoveChild.call(this, child) as T; - // container may had been removed while app unmounting if the removeChild action was async - const container = appWrapperGetter(); - const attachedElement = dynamicScriptAttachedCommentMap.get(child as any) || child; - if (container.contains(attachedElement)) { - return rawRemoveChild.call(container, attachedElement) as T; + try { + let attachedElement: Node; + switch (tagName) { + case LINK_TAG_NAME: { + attachedElement = (dynamicLinkAttachedInlineStyleMap.get(child as any) as Node) || child; + break; } + + case SCRIPT_TAG_NAME: { + attachedElement = (dynamicScriptAttachedCommentMap.get(child as any) as Node) || child; + break; + } + + default: { + attachedElement = child; + } + } + + // container may had been removed while app unmounting if the removeChild action was async + const appWrapperGetter = appWrapperGetterGetter(child as any); + const container = appWrapperGetter(); + if (container.contains(attachedElement)) { + return rawRemoveChild.call(container, attachedElement) as T; } } catch (e) { console.warn(e);