美文网首页
120.乾坤css隔离机制

120.乾坤css隔离机制

作者: wo不是黄蓉 | 来源:发表于2022-11-16 21:56 被阅读0次
    乾坤css隔离机制.jpg

    乾坤解决样式隔离的几种方法:

    • 确保单实例场景子应用之间的样式隔离,但是无法确保主应用跟子应用、或者多实例场景的子应用样式隔离
    start({sandbox : true})//沙箱隔离,
    

    判断沙箱隔离的方法,发现直接设置sandbox=true是不行的,需要使用experimentalStyleIsolation这个属性配置。

    export function isEnableScopedCSS(sandbox: FrameworkConfiguration['sandbox']) {
      if (typeof sandbox !== 'object') {
        return false;
      }
      if (sandbox.strictStyleIsolation) {
        return false;
      }
      return !!sandbox.experimentalStyleIsolation;
    }
    
    • { strictStyleIsolation: true }开启严格的样式隔离模式 ,shawod dom'
    start({shadow:{ strictStyleIsolation: true }})
    

    直观的可以看到,子应用的代码不是正常的dom,上面显示的shadow-root,先不管shadow-root是什么,接着往下看


    image.png
    • experimentalStyleIsolation给子应用加上自定义的data-xxx属性,相当于vue中我们通常使用的scoped,从生成的代码中可以看出来
      1668608526445.png

    在主应用中,通过配置sandbox做到样式隔离

    start({ sandbox: { strictStyleIsolation: true } });
    

    在start得时候配置得参数,但是只有等到start函数里面 frameworkStartedDefer.resolve() 之后才能往下执行.
    registerMicroApps只是准备好注册的应用配置信息,只有等调用了start方法之后,才会走registerApplication相关逻辑,registerApplicationsingle-spa中的一个主要的方法,乾坤实现微应用也主要用的是single-spa这个库

    start 方法

    export function start(opts: FrameworkConfiguration = {}) {
      frameworkConfiguration = { prefetch: true, singular: true, sandbox: true, ...opts };
      startSingleSpa({ urlRerouteOnly });
      started = true;
        //执行loadApp相关逻辑
      frameworkStartedDefer.resolve();
    }
    

    修改appid得代码是在single-spa中还是qiankun中得?->qiankun,在处理模板的时候使用shadow dom实现的

    loadApp中使用严格样式隔离的代码

    export async function loadApp<T extends ObjectType>(
      app: LoadableApp<T>,
      configuration: FrameworkConfiguration = {},
      lifeCycles?: FrameworkLifeCycles<T>,
    ): Promise<ParcelConfigObjectGetter> {
          const strictStyleIsolation = typeof sandbox === 'object' && !!sandbox.strictStyleIsolation;
          let initialAppWrapperElement: HTMLElement | null = createElement(
            appContent,
            strictStyleIsolation,
            scopedCSS,
            appInstanceId,
          );
    }
    

    createElement方法相关代码,我们在start方法中配置了strictStyleIsolation严格样式模式,因此会走到strictStyleIsolation判断相关逻辑里面,从这里面就可以看到创建shadow dom相关代码

    function createElement(
      appContent: string,
      strictStyleIsolation: boolean,
      scopedCSS: boolean,
      appInstanceId: string,
    ): HTMLElement {
      const containerElement = document.createElement('div');
      containerElement.innerHTML = appContent;
      // appContent always wrapped with a singular div
      const appElement = containerElement.firstChild as HTMLElement;
         //绝对样式隔离相关代码
      if (strictStyleIsolation) {
          //使用shadow做样式隔离
          const { innerHTML } = appElement;
          appElement.innerHTML = '';
          let shadow: ShadowRoot;
    
          if (appElement.attachShadow) {
            shadow = appElement.attachShadow({ mode: 'open' });
          } else {
            // createShadowRoot was proposed in initial spec, which has then been deprecated
            shadow = (appElement as any).createShadowRoot();
          }
          shadow.innerHTML = innerHTML;
    
      }
    
     //...scopedCSS判断相关代码
    
      return appElement;
    }
    

    使用scopedCss做到样式隔离

    前面入口出的逻辑和shadow大致相同,scopedCss代码也很好理解,就是先查找要添加的应用是否有相应的属性,没有的话给子应用容器添加一个属性。然后遍历每个style标签,在每个style标签代码前面增加属性选择符,通过属性选择符+样式来确保样式不被重写

      if (scopedCSS) {
        const attr = appElement.getAttribute(css.QiankunCSSRewriteAttr);
        if (!attr) {
            //这边attr生成的属性:data-qiankun :vue2App
          appElement.setAttribute(css.QiankunCSSRewriteAttr, appInstanceId);
        }
    
        const styleNodes = appElement.querySelectorAll('style') || [];
        forEach(styleNodes, (stylesheetElement: HTMLStyleElement) => {
          css.process(appElement!, stylesheetElement, appInstanceId);
        });
      }
    

    css.process方法

    export const process = (
      appWrapper: HTMLElement,
      stylesheetElement: HTMLStyleElement | HTMLLinkElement,
      appName: string,
    ): void => {
      // lazy singleton pattern
      if (!processor) {
        processor = new ScopedCSS();
      }
      const mountDOM = appWrapper;
      if (!mountDOM) {
        return;
      }
      const tag = (mountDOM.tagName || '').toLowerCase();
    
      if (tag && stylesheetElement.tagName === 'STYLE') {
          //生成前缀
        const prefix = `${tag}[${QiankunCSSRewriteAttr}="${appName}"]`;
        processor.process(stylesheetElement, prefix);
      }
    };
    

    什么是shadow dom?

    可以将标记结构,样式和行为隐藏起来,和页面上其他代码相隔离。

    shadow dom的一个demo:

    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8" />
        <meta http-equiv="X-UA-Compatible" content="IE=edge" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>Document</title>
      </head>
      <style>
        h1 {
          color: red;
        }
      </style>
      <body>
        <h1>hello 这个主页中的内容</h1>
        <div id="box"></div>
      </body>
    
      <script>
        window.onload = function () {
          let box = document.getElementById("box");
          //开启shadow dom
          box.attachShadow({ mode: "open" });
          let shadow = box.shadowRoot;
          var h1 = document.createElement("h1");
          h1.innerHTML = "world 这是shaow dom中的内容";
          var style = document.createElement("style");
          style.textContent = `h1{color:green}`;
          shadow.appendChild(style);
          shadow.appendChild(h1);
        };
      </script>
    </html>
    
    
    image.png
    乾坤框架系列学习
    01.学习微前端架构-乾坤
    02.资源加载过程分析
    03.乾坤css加载机制
    04.乾坤js隔离机制
    乾坤沙箱实现原理

    相关文章

      网友评论

          本文标题:120.乾坤css隔离机制

          本文链接:https://www.haomeiwen.com/subject/ggmexdtx.html