产生原因
- 子应用样式后加载,由于父子应用样式同名,导致了样式覆盖
思路
以父应用elementUi2.0为例,子应用elementPlus为例
- 查看官方文档,官方文档给出了两个解决方案,一一实施,发现都未解决
start(opts?)
Options {
sandbox - boolean | { strictStyleIsolation?: boolean, experimentalStyleIsolation?: boolean } - 可选,是否开启沙箱,默认为 true。
}
当配置为 { strictStyleIsolation: true } 时表示开启严格的样式隔离模式。这种模式下 qiankun 会为每个微应用的容器包裹上一个 shadow dom 节点,从而确保微应用的样式不会对全局造成影响。
当 experimentalStyleIsolation 被设置为 true 时,qiankun 会改写子应用所添加的样式为所有样式规则增加一个特殊的选择器规则来限定其影响范围。
-
方案一:利用shadow dom,并不是无脑对方案,得做适配。原因:子应用选择器全部失效,其他问题。
-
方案二: 试验性的方案,使用后,没有解决挂载于body下面的组件样式,会有各种问题。
-
父应用替换所有class前缀
- 考虑用webpack插件替换所有的class前缀,尝试了,但对webpack插件掌握程度不够,无法解决
-
给子应用样式选择器添加前缀(类似于官方的方案二)
- 利用postcss plugin解决
- 遇到的问题:有些样式无法用该方法解决,如挂载于body下面的组件样式
什么是postcss
- 是一个允许使用 JS 插件转换样式的工具,一个解析器,一种对css编译的工具。
- PostCSS 接收一个 CSS 文件并提供了一个 API 来分析、修改它的规则(通过把 CSS 规则转换成一个抽象语法树的方式)。
和less sass的区别
less sass 是预处理器,用来支持扩充css语法。
postcss 既不是 预处理器也不是 后处理器,其功能比较广泛,而且重要的一点是,postcss可以和less/sass结合使用
我们能用来干什么
- 解决全局 CSS 的问题
- 提前使用先进的 CSS 特性
- 更佳的 CSS 可读性
- 提示器(Linters
- 更多
postcss的应用
- autoprefixer
- stylelint
- …等等200多个插件
抽象语法树(可能由以下几部分构成)
- Root: ast的根节点,代表着每个css文件
- AtRule: 每个以@开头选择器所在的Rule
- Rule: css选择器和声明共同组成了一个CSS规则,例如 input, button {font-size: 20px;}其中的大括号就是声明
- Declaration: 声明,key-value pair like color: black;
- Comment: 注释,可存在于选择器、@选择器的参数、css样式键值对的value中,节点中的注释会被保存在node的raws属性中
AST示例
Root {
raws: { semicolon: false, after: '' },
type: 'root',
nodes: [
Rule {
raws: [Object],
type: 'rule',
nodes: [Array],
parent: [Circular *1],
source: [Object],
selector: '.tab-dialog'
},
],
source: {
input: Input {
css: '.tab-dialog {\n' +
' position: absolute;\n' +
' top: 0;\n' +
' bottom: 0;\n' +
' right: 40px;\n' +
' width: 500px;\n' +
' background: white;\n' +
' box-shadow: 0 0 8px rgba(0, 0, 0, 0.2);\n' +
' display: flex;\n' +
' flex-direction: column;\n' +
'}\n' +
'.tab-dialog-header {\n' +
' display: flex;\n' +
' justify-content: space-between;\n' +
' font-size: 20px;\n' +
' padding: 10px;\n' +
'}\n' +
'.tab-dialog-header .el-icon-close {\n' +
' cursor: pointer;\n' +
'}\n' +
'.tab-dialog-main {\n' +
' flex: 1;\n' +
' overflow: auto;\n' +
' padding: 0 10px;\n' +
' box-sizing: border-box;\n' +
'}\n' +
'.tab-dialog-footer {\n' +
' text-align: right;\n' +
' padding: 10px;\n' +
'}',
hasBOM: false,
file: '/Users/mac/Desktop/workspace/fe-dgp-web/v3/src/views/data/taskEdit/TabDialog.vue'
},
start: { line: 1, column: 1 }
}
}
大致流程
- 将CSS解析成抽象语法树(AST树)
- 将AST树”传递”给任意数量的插件处理
- 将处理完毕的AST树重新转换成字符串
Source string → Tokenizer → Parser → AST → Processor → Stringifier
postcss插件的开发
const postcss = require('postcss')
module.exports = postcss.plugin('postcss-add-css-prefix', function (opts = {}) {
const { prefix = '' } = opts
// 接收两个参数,第一个是每个css文件的ast,第二个参数中可获取转换结果相关信息(包括当前css文件相关信息)
function plugin(css, result) {
if (!prefix) return // 没传入prefix,不执行下面的逻辑
css.walkRules((rule) => {
// 遍历当前ast所有rule节点
const { selector } = rule
// 加了个flag,防止节点更新后重复执行该逻辑进入死循环
if (
selector.includes('.el-') &&
!selector.includes(prefix) &&
!rule.flag &&
!selector.includes('.el-popper')
) {
rule.flag = true
const clone = rule.clone()
// console.log(selector)
clone.selector = `${prefix} ${selector}`
rule.replaceWith(clone)
} else if (
!selector.includes(prefix) &&
!rule.flag &&
selector.includes('.el-popper')
) {
// 处理挂载于body下面节点的样式
}
})
}
return plugin
})