qiankun- CSS隔离

1,165 阅读6分钟

1.背景

qianukn中需要解决的样式问题就是主子应用之间的样式污染冲突问题。即当qiankun中当主应用加载子应用的时候,主应用和子应用之间的css样式会产生冲突(污染),具体可能出现的主子应用的样式情况如下:
1.未对子应用进行样式隔离时:子应用的样式如果和主应用的样式重名,会覆盖主应用样式,造成污染。
(问题原因:后加载的样式优先级高)
2.对子应用的样式进行隔离处理:当子应用中出现弹窗、抽屉、popover等组件,这些组件是需要插入到主应用body下的dom元素,这就导致原来属于子应用的dom被插入到了主应用的dom中,原来子应用的样式就会丢失或者凑巧使用了主应用的样式。
(问题原因:开启了css隔离之后,子应用样式的作用域只是在子应用中,而弹窗这类组件最后会被插入到最外层的body下,已经脱离了子应用的作用域范围了)
3.添加important情况:主应用的样式污染了子应用
(问题原因:主应用的样式添加了 !important 属性或 >>> 穿透属性)

2.qiankun提供的css隔离沙箱方案(2种)

start函数中配置sandbox属性,通过配置sanbox来确认需要css采用的隔离方案

1. 严格css沙箱模式(strictStyleIsolation: true)

这种模式的原理就是在子应用外面再套一层 shadow DOM,进而将子应用和主应用之间的样式进行隔离,隔离效果类似于iframe。 这种可以解决主应用和子应用样式冲突的问题,但是对于弹窗问题依然不能解决,因为弹窗问题的麻烦点在于:子应用的弹窗是挂载整个html的body下面的,因此就会导致子应用的弹窗最终会挂载主应用那一层的body下面,这种情况shadow-dom的隔离是没办法处理的(因为弹窗已经跑到了shadow-dom之外了)严格模式沙箱有个问题:子应用的弹窗因为shadow dom的严格模式,所以找不到子应用的body,导致整个会跑到屏幕之外

2.实验性css沙箱(experimentalStyleIsolation: true)

这种实验性模式是通过css-module来实现隔离的,这种隔离的原理类似于vue的scoped(本质上,vue中scoped主要通过[postcss]实现的),就是在对应的标签上增加data-key属性,这样就是的每个div都具有独一无二的类名上。即,每个子应用都会有对应的data-子应用1-key的属性(div[data-qiankun-microName]),这样来达到样式隔离的效果。 但是这样仍然存在的问题:弹窗样式问题的处理无法解决。因为弹窗是塞到body下的,这样当子应用的弹窗也会找到主应用的body下面,而不是子应用的下面,因此导致弹窗的样式沿用了主应用的样式

sandbox: { 
    strictStyleIsolation?: boolean, 
    // 开启严格的样式隔离模式。这种模式下 qiankun 会为每个微应用的容器包裹上一个 [shadow dom]节点,从而确保微应用的样式不会对全局造成影响。
    experimentalStyleIsolation?: boolean
    // 设置实验性的样式隔离特性,即在子应用下面的样式都会包一个特殊的选择器规则来限定其影响范围
}

// 假设应用名是:vueApp
.app-main {
  font-size: 14px;
}
div[data-qiankun-vueApp] .app-main { // 前面加了一个前置类名,这就是实验性css沙箱的作用
  font-size: 14px;
}

image.png

总结:

这两种方案都可以实现样式隔离,只是底层实现的原理不一样,一个是借助于web-component技术中的shadow-dom,然后将子应用append在这个shadow-dom中,进而来实现隔离;另一个是通过类似vue-scoped技术(这里需要 v ) 那么对于弹窗样式问题如何解决? 因为项目中采用的是antd- UI组件,所以可以利用 ConfigProvider 设置好每一个组件的类名前缀,然后配置less-loader解析

其他相关问题:
1.vue中的scoped中原理:postcss
功能:scoped的作用是对vue组件内部的样式与外部进行隔离。
原理:对组件内部每个dom上添加data-v-hash属性,然后生成css样式的时候对应的类名就会变为:“ .类名[data-v-hash] ”,这样就实现了dom和类名一一对应。
例如,图一中我添加了scoped, image.png 那么经过postcs转化之后就变成了第二张图:(当前scoped组件内所有的类名都会加上这个[data-v-hash值],上图中vue组件中的.example类名 => 本质上是 .example[data-v-hash] ) image.png 此外,对于父子组件是否分别添加scoped属性,有三种情况,具体表现参考:blog.csdn.net/qq_40544291…
实现方式:post-css。我们都知道babel是将高级程序语言转化为可以被js识别的AST语法树,然后对这个语法树及进行解析修改,进而转化为可以被浏览器识别的低级语言。同样的功能,css不具有对应的逻辑语言功能,因此我们可以通过post-css对css的语法进行解析构建对应的css语法树,这样我们就可以使用js对这个css语法书进行解析和修改,然后再转化成css。post-css和babel一样,都是一种将一种不能被js识别的语言转化为可以被js分析的AST语法树。通过js对语法树的修改后来再次还原原来语言。总之,css不具有逻辑语言,因此我们通过post-css将css转化为可以被js识别的语法树然后通过逻辑去修改,这里的修改就是交给对应的postcss插件修改(比如增加类名前缀等),然后再还原。
post-css的其他作用:
(1) 移动端 rem适配 (使用postcss-pxtorem)
(2)
(3)
2.vue中deep原理
3.antd中使用ConfigProvider和less-loader配合使用的原理

3.css工程化手段

  1. css-module
    一个 CSS 文件就是一个独立的模块。
  2. css-in-js
    在 JS 中写 CSS。
  3. shadow dom
    css隔离按照原理有两种方式: 1.css类名唯一性: bem css-in-js:react css module: vue-scoped: 预处理器less 2.css强制隔离:shadowDom