ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

微前端-03

2022-07-18 15:35:37  阅读:173  来源: 互联网

标签:03 const 前端 rule prefix html templateStyle app


接着上面两篇文章,继续学习微前端,本章主要内容如下:

  •   微应用的样式隔离

首先,为什么需要做样式隔离?

因为微应用的话,是使用html entry加web components来实现的,所以说,这种方式实现的微应用并不具有iframe那种天然的样式隔离特性,

基于前两篇的分析,我们可以看出,我们是将html entry中的 资源(style,link,script)抽离出来,然后再挂载到micro-app-head中的,由于微应用

的样式挂载时机是出于基座应用后面的,所以如果基座应用与微应用具有相同的选择器,则微应用会覆盖掉基座应用的选择器。

借用一张图片来说明:

 

 

 如何解决这个问题?

既然我们可以拿到资源的代码,那么就在获取代码之后进行处理,思路是,将对应的样式代码,进行正则匹配替换,统一加上前缀,

 

const prefix = `micro-app[name=${appName}]`;

 

这样的话,每个微应用由于appName不同,也就不会相互影响了。

具体实现是采用cssRules+递归来处理不同的cssRule来实现,代码如下:

let templateStyle;

/**
 * 进行样式隔离
 * @param {HTMLStyleElement}
 * @param {string}
 */

export default function scopedCSS(styleElement, appName) {
    // 前缀
    const prefix = `micro-app[name=${appName}]`;
    if (!templateStyle) {
        templateStyle = document.createElement("style");
        document.body.appendChild(templateStyle);
        // 设置样式表无效
        templateStyle.sheet.disabled = true;
    }

    if (styleElement.textContent) {
        templateStyle.textContent = styleElement.textContent;
        styleElement.textContent = scopedRule(
            Array.from(templateStyle.sheet?.cssRules ?? []),
            prefix
        );
        // 清空模板style内容;
        templateStyle.textContent = "";
    } else {
        const observe = new MutationObserver(function () {
            // 断开监听
            observe.disconnect();
            styleElement.textContent = scopedRule(
                Array.from(templateStyle.sheet?.cssRule ?? []),
                prefix
            );
        });
        observe.observe(styleElement, { childList: true });
    }
}

function scopedRule(rules, prefix) {
    let result = "";
    for (const rule of rules) {
        switch (rule.type) {
            case 1:
                result += scopedStyleRule(rule, prefix);
                break;
            case 4:
                result += scopedPackRule(rule, prefix, "media");
                break;
            case 12:
                result += scopedPackRule(rule, prefix, "supports");
            default:
                result += rule.cssText;
                break;
        }
    }
    return result;
}

function scopedPackRule(rule, prefix, packName) {
    const result = scopedRule(Array.from(rule.cssRules), prefix);
    return `@${packName} ${rule.conditionText}{ ${result} }`;
}

function scopedStyleRule(rule, prefix) {
    const { selectorText, cssText } = rule;
    // 处理顶层选择器,如 body,html 都转换为 micro-app[name=xxx]
    if (/^((html[\s>~,]+body)|(html|body|:root))$/.test(selectorText)) {
        return cssText.replace(
            /^((html[\s>~,]+body)|(html|body|:root))$/,
            prefix
        );
    } else if (selectorText === "*") {
        return cssText.replace("*", `${prefix} *`);
    }

    const builtInRootSelectorRE =
        /(^|\s+)((html[\s>~]+body)|(html|body|:root))(?=[\s>~]+|$)/;

    return cssText.replace(/^[\s\S]+{/, (selector) => {
        return selector.replace(/(^|,)([^,]+)/g, (all, $1, $2) => {
            if (builtInRootSelectorRE.test($2)) {
                return all.replace(builtInRootSelectorRE, prefix);
            }
            return `${$1} ${prefix} ${$2.replace(/^\s*/, "")}`;
        });
    });
}

 

然后在获取到资源的时候进行调用就可以了,举个例子:

 

function fetchLinksFromHtml(app, microAppHead, htmlDom) {
    const linkEntries = Array.from(app.source.links.entries());
    // 通过fetch请求所有css资源
    const fetchLinkPromise = [];
    for (const [url] of linkEntries) {
        fetchLinkPromise.push(fetchSource(url));
    }

    Promise.all(fetchLinkPromise)
        .then((res) => {
            for (let i = 0; i < res.length; i++) {
                const code = res[i];
                // 拿到css资源后放入style元素并插入到micro-app-head中
                const link2Style = document.createElement("style");
                link2Style.textContent = code;
                console.log("link2style", link2Style.textContent);
                scopedCSS(link2Style, app.name);
                microAppHead.appendChild(link2Style);

                // 将代码放入缓存,再次渲染时可以从缓存中获取
                linkEntries[i][1].code = code;
            }

            // 处理完成后执行onLoad方法
            app.onLoad(htmlDom);
        })
        .catch((e) => {
            console.error("加载css出错", e);
        });
}

 

 

 

 

 

 

标签:03,const,前端,rule,prefix,html,templateStyle,app
来源: https://www.cnblogs.com/alone4436/p/16490581.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有