当前位置:首页 > 前端开发 > 正文内容

qiankun 的 CSS 沙箱阻隔机制

邻居的猫1个月前 (12-09)前端开发269

为什么需求CSS沙箱

在 qiankun 微前端结构中,由于每个子运用的开发和布置都是独立的,将主/子运用的资源整合到一一起,简单呈现款式抵触的问题

因而,需求 CSS 沙箱来处理款式抵触问题,完成主运用以及各子运用之间的款式阻隔,保证各自的款式独立运转,互不搅扰

工程化手法

已然 CSS 沙箱是用来处理款式抵触的问题,那假如我经过工程化手法保证每个款式挑选器称号都是仅有的,这样是不是就不需求 CSS 沙箱了?

运用工程化手法来生成仅有的 CSS 类名,常见处理计划有:

  1. BEM:不同项目用不同的前缀或命名规矩来保证类名仅有性,防止款式抵触,详见 BEM命名标准
  2. CSS Module:经过构建东西装备(详见 webpack 启用 css-loader)在构建进程中主动生成仅有的类名。对了,vue3 中<style module> 标签也会被编译为 CSS Module,详见 Vue.js - 单文件组件|CSS功用
  3. CSS-in-JS: 在 JS 中界说 CSS 款式块,注入到 DOM 中,详见 CSS-in-JS 攻略

可是这些计划都存在一些问题:

  1. 前史包袱:关于老旧项目,尤其是那些未选用现代工程化手法的项目,修正现有代码以支撑新的款式办理计划(如 BEM 或 CSS-in-JS)需求很多的重构作业
  2. 第三方库:即便你保证了自己的款式挑选器仅有,第三方库的款式仍或许会导致抵触

明显,工程化手法只能处理一部分问题,在实践运用中,或许需求结合运用工程化手法和 CSS 沙箱,以应对不同的款式办理需求

天地沙箱

天地现在存在三种 CSS 阻隔机制,分别是动态款式阻隔、影子DOM沙箱和效果域沙箱

  1. 动态款式阻隔:qiankun 默许敞开,能够保证单实例场景子运用之间的款式阻隔,可是无法保证主运用跟子运用、或许多实例场景的子运用款式阻隔
  2. 影子DOM沙箱(Shadow DOM):手动敞开 ,qiankun 会为每个微运用的容器包裹上一个 shadow dom 节点,然后保证微运用的款式不会对大局构成影响
  3. 效果域沙箱(Scope CSS):手动敞开 ,qiankun 会改写子运用所添加的款式,为一切款式规矩添加一个特别的挑选器规矩来约束其影响规模

你或许想问,开关在呢❓怎么手动敞开我想要的沙箱机制❓❓❓

在这个 天地API - start({ }) 中,有一个可选参数 sandbox,用于操控是否敞开沙箱以及敞开哪种沙箱

  • true:默许值,敞开动态款式阻隔
  • { strictStyleIsolation: true }:敞开影子DOM沙箱
  • { experimentalStyleIsolation: true }:敞开效果域沙箱

动态款式阻隔

天地会默许敞开此沙箱

能够保证单实例场景子运用之间的款式阻隔,可是无法保证主运用跟子运用、或许多实例场景子运用之间的款式阻隔

完成原理是当子运用被加载时,其对应的款式会被注入到页面中;当子运用被卸载时,qiankun 会主动移除其款式,保证页面的款式环境坚持洁净

动态款式阻隔尽管能够供给很好的阻隔效果,但往往存在一些约束条件,所以在实践的运用中根本无法独自满意用户的需求

关于新的子运用,运用动态款式阻隔 + 工程化手法两种计划结合的办法,根本能够处理款式抵触的问题

Shadow DOM 沙箱

手动敞开,敞开代码如下

import { registerMicroApps, start } from 'qiankun'

registerMicroApps([...]) // 注册子运用

start({ 
  sandbox: { strictStyleIsolation: true  }  // 敞开 Shadow DOM 沙箱
}) 

这种形式下 qiankun 会为每个微运用的容器包裹上一个 shadow dom 节点,然后保证微运用的款式不会对大局构成影响

Shadow DOM是什么?

Shadow DOM 是 Web Components 技能的一部分,它答应开发者创立一个关闭的 DOM 树,这个 DOM 树的款式和脚本与页面的主 DOM 树是阻隔的。经过 Shadow DOM,能够保证子运用的款式和脚本不会影响到主运用或其他子运用,然后防止抵触和搅扰

Shadow DOM,能够理解为是存在于 DOM 中的 DOM

记住!影子DOM 是独立存在的 DOM,有自己的效果域集,外部的装备不会影响到内部,内部的装备也不会影响外部

影子 DOM 答应将躲藏的 DOM 树附加到惯例 DOM 树中的元素上——这个影子 DOM 始于一个影子根,在其之下你能够用与一般 DOM 相同的办法附加任何元素

这儿有一些影子 DOM 术语:

  • 影子宿主(Shadow host): 影子 DOM 附加到的惯例 DOM 节点
  • 影子树(Shadow tree): 影子 DOM 内部的 DOM 树
  • 影子鸿沟(Shadow boundary): 影子 DOM 停止,惯例 DOM 开端的当地
  • 影子根(Shadow root): 影子树的根节点

说了这么多,那怎么创立创立影子 DOM ?
咱们能够调用宿主上的 attachShadow() 来创立影子 DOM

咱们结合 天地小demo 实践演示一下,影子DOM到底有什么效果?

ok!咱们创立了一个 qiankun 项目,现在主运用和子运用根节点类名相同,都是 .App,主运用根节点背景色设置为黑色,子运用根节点背景色设置为赤色

由于 qiankun 默许的动态款式阻隔机制存在缺点,无法保证主运用和子运用之间的款式阻隔,咱们发现,子运用污染了主运用的背景色款式

启用 Shadow DOM沙箱阻隔机制,Later~,一切正常

完成原理

这儿咱们完成一下 Shadow DOM 沙箱机制的中心逻辑,对应天地的源代码在createElement办法,能够看这儿 - Shadow DOM沙箱源代码

其原理也很简单,便是将子运用模板包裹在 Shadow DOM 中,使其构成一个独立的款式效果域,保证其款式阻隔

<body>
  <div id="root">qiankun 是一个依据 single-spa 的微前端完成库</div>
  <script>
    // 子运用的模版字符串
    const template = `<div id="qiankun-xxx">
                        <div id="app">Shadow DOM 沙箱</div>    
                        <style>div{color:red}</style>
                    </div>`

    function createElement(appContent) {
      const containerElement = document.createElement('div')
      containerElement.innerHTML = appContent
      const appElement = containerElement.firstChild // 影子宿主(template模版字符串转化成了实在的dom)

      const shadow = appElement.attachShadow({ // 影子DOM(调用宿主上的 attachShadow() 来创立影子 DOM)
        mode: 'open',
      })
      shadow.innerHTML = appElement.innerHTML // 给Shadow DOM附加宿主节点下的内容

      appElement.innerHTML = ''
      return appElement
    }

    document.body.appendChild(createElement(template))
  </script>
</body>

尽管 Shadow DOM 是一个强壮的技能 ,但在某些场景下,它并不是一个完美的处理计划

比方,越界的 DOM 操作,在实践运用中,子运用或许会有操作主文档 DOM 的需求,比方动态地向主文档document 添加大局组件、弹窗等。这些操作会创立 Shadow DOM 之外的元素,Shadow DOM 的内部款式也就无法对这些元素收效

依据 ShadowDOM 的严厉款式阻隔并不是一个能够无脑运用的计划,大部分情况下都需求接入运用做一些适配后才干正常在 ShadowDOM 中运转起来(比方 react 场景下需求处理这些 问题,运用者需求清楚敞开了 strictStyleIsolation 意味着什么 - 摘录自 qiankun 文档

Scope CSS (Scoped CSS)

手动敞开,敞开代码如下

import { registerMicroApps, start } from 'qiankun'

registerMicroApps([...]) // 注册子运用

start({ 
  sandbox: { experimentalStyleIsolation: true  }  // 敞开效果域沙箱
}) 

这是 qiankun 一个实验性的款式阻隔特性,它的中心思维是经过给子运用中的一切款式挑选器添加一个仅有的前缀挑选 div[data-qiankun="xxx"],来约束这些款式的效果规模

关于一个挑选器,假如需求约束它的效果规模,能够运用组合挑选器的办法。在当时挑选器A前面加一个挑选器B,使得挑选器A只效果在挑选器B内部的节点

改写后的代码会表达为如下结构

// 假定 registerMicroApps 办法注册的子运用 name 是 react16
.app-main {
  font-size: 14px;
}

// 改写后
div[data-qiankun="react16"] .app-main {
  font-size: 14px;
}

完成原理

提取和解析款式:当一个子运用被加载时,qiankun 会提取子运用中的一切 <style> 标签内嵌款式和 <link> 标签引进的外部款式,并对其进行解析,获取一切的 CSS 规矩

重写款式规矩:qiankun 给每个子运用的包裹容器新增仅有标识符 data-qiankun 特点,值为经过 registerMicroApps API 注册子运用的 name;然后修正子运用的款式挑选器,添加前缀挑选器 div[data-qiankun="xxx"],重写挑选器

由于效果域沙箱不能直接修正 link 标签引进的外部款式,所以会把 link 外部款式转化为style 内嵌款式,再给其添加前缀

对应天地源代码的进口是createElement办法,能够看这儿 - Scope CSS沙箱源代码

function createElement(
appContent: string,
strictStyleIsolation: boolean,
scopedCSS: boolean,
appName: string,
): HTMLElement {
  const containerElement = document.createElement('div');
  containerElement.innerHTML = appContent;
  const appElement = containerElement.firstChild as HTMLElement;
  
  /**
   * CSS款式抵触的处理办法
   * 1. shadowDOM
   * 2. scoped CSS
   */
  if (strictStyleIsolation) {
    // ... shadowDOM 沙箱逻辑
  }
  
  if (scopedCSS) {
    // 常量 css.QiankunCSSRewriteAttr = 'data-qiankun'
    const attr = appElement.getAttribute(css.QiankunCSSRewriteAttr);
    if (!attr) {
      // 给子运用的包裹容器新增 data-qiankun 特点,值为经过 registerMicroApps 注册子运用的 name
      appElement.setAttribute(css.QiankunCSSRewriteAttr, appName);
    }

    // 遍历子运用的一切款式,修正其款式挑选器,添加前缀挑选器 div[data-qiankun="xxx"]
    const styleNodes = appElement.querySelectorAll('style') || [];
    forEach(styleNodes, (stylesheetElement: HTMLStyleElement) => {
      css.process(appElement!, stylesheetElement, appName);
    });
  }
  
  return appElement;
}

缺乏的话,应该是解析子运用的 style 款式,并为每个挑选器添加前缀。这一进程在子运用的加载和烘托时会添加额定的核算开支,尤其是在款式表很大或许包括很多挑选器的情况下,或许会影响页面的初始加载功能

沙箱计划

实践的作业中,挑选适宜的沙箱计划需求依据详细的场景和需求来决议。 以下是一些常见的场景及其对应的沙箱挑选

单实例形式

单实例形式指的是一次仅加载一个子运用的场景,这种形式下子运用之间不会并发运转,防止了一起多个运用运转导致的抵触

在这种形式下,动态款式阻隔+ 工程化手法(如 BEM 命名标准、CSS Modules)一般就能满意大部分需求。由于在单实例形式中,不需求担心子运用之间的款式和脚本抵触问题。

多实例形式

在多实例形式下,多个子运用或许一起加载和运转,子运用之间的款式和脚本简单发生抵触

在这种形式下,需求更强的阻隔性。能够运用 效果域沙箱(Scoped CSS Sandbox)+ Shadow DOM 沙箱 的组合

参阅文档

GitHub - careyke/frontend_knowledge_structure: qiankun中CSS沙箱的完成

终究什么是Shadow DOM?

运用影子 DOM - Web API | MDN

扫描二维码推送至手机访问。

版权声明:本文由51Blog发布,如需转载请注明出处。

本文链接:https://www.51blog.vip/?id=413

标签: 前端架构
分享给朋友:

“qiankun 的 CSS 沙箱阻隔机制” 的相关文章

html网页特效代码,html官方下载免费版

html网页特效代码,html官方下载免费版

创建一个HTML网页特效需要结合HTML、CSS和JavaScript。下面是一个简单的示例,创建一个带有旋转特效的按钮:```html旋转特效按钮 .rotatebutton { padding: 10px 20px; backgroundcolor: 4CAF50; colo...

html5网页前端设计,打造搜索引擎友好与用户体验并重的现代网页

html5网页前端设计,打造搜索引擎友好与用户体验并重的现代网页

3. 表单改进:HTML5 对表单进行了许多改进,包括新的输入类型(如日期、时间、颜色、范围等)和表单验证功能。这些改进使得创建更友好、更易于使用的表单变得更加容易。4. Canvas 和 SVG:HTML5 引入了 `` 元素,它提供了一个绘制图形的画布,使得开发者可以创建动态的图形和动画。同时,...

html如何打开,HTML壅壃控壄嬙墼有墿壂

html如何打开,HTML壅壃控壄嬙墼有墿壂

HTML(超文本标记语言)本身并不是一个程序或应用,而是一种用于创建网页的标准标记语言。因此,您不能“打开”HTML,而是需要将其嵌入到网页中,然后通过浏览器来查看和交互。要查看一个HTML文件,您需要执行以下步骤:1. 创建HTML文件:使用文本编辑器(如Notepad 、Sublime Tex...

css内阴影,CSS内阴影的强大应用与实现方法

css内阴影,CSS内阴影的强大应用与实现方法

CSS内阴影(inner shadow)是CSS3中新增的一种阴影效果,它允许你为元素添加向内凹陷的阴影效果。与传统的盒阴影(boxshadow)不同,内阴影是在元素的内部创建的,使得阴影看起来像是元素的一部分。要使用CSS内阴影,你需要使用`boxshadow`属性,并指定相应的值。内阴影的语法与...

html调整字体大小,html网页代码生成器

1. 使用CSS样式:你可以通过CSS来设置字体大小。例如,你可以使用`fontsize`属性来设置字体大小。例如,`这是16像素的字体。3. 使用百分比:你可以使用百分比来设置字体大小。例如,`这是120%的字体。4. 使用em单位:em单位是相对于当前字体大小的单位。例如,`这是1.2em的字体...

css制作,css官网入口

css制作,css官网入口

当然可以!不过,为了更有效地帮助您,请您具体说明您想使用 CSS 完成什么任务或实现什么效果。例如,您是想制作一个简单的布局、按钮、导航栏,还是想要实现某种特定的动画效果?请提供更多的细节,这样我可以为您提供更具体的指导。 CSS制作:从入门到精通的实用指南 一、CSS简介CSS,即层叠样式表(Ca...