qiankun落地问题
CSS 隔离
qiankun 加载子项目 css 样式机制大体为:挂载子应用时将子应用的 css 样式以 style 标签的形式插入并做快照,卸载子应用时再将快照内的 style 样式删除。
所以在加载子应用期间,若未开启 css 沙箱隔离,后加载的这些样式,可能会对整个系统的样式产生影响,对此,qiankun 提供了两种 css 沙箱功能,可以将子应用的样式包裹在沙箱容器内部,以此来达到样式隔离的目的。
qiankun 严格沙箱
在加载子应用时,添加 strictStyleIsolation: true 属性,实现形式为将整个子应用放到 Shadow DOM 内进行嵌入,完全隔离了主子应用
缺点:
- 子应用的弹窗、抽屉、popover 因找不到主应用的 body 会丢失,或跑到整个屏幕外
- 主应用不方便去修改子应用的样式
实验性沙箱
在加载子应用时,添加 experimentalStyleIsolation: true 属性,实现形式类似于 vue 中 style 标签中的 scoped 属性,qiankun 会自动为子应用所有的样式增加后缀标签,如:div[data-qiankun-microName]
缺点:
- 子应用的弹窗、抽屉、popover因插入到了主应用的body,所以导致样式丢失或应用了主应用了样式。例如 elementUI 的 Popover 弹出框、Dropdown 下拉菜单、嵌套的 Drawer 、嵌套的 dialog 等
找到根本的原因,其实就好解决了。
在乾坤出现之前,饿了么组件就已经用的很多了,但是当时的组件设计师们,也没有考虑到后续会有这种微前端集成的需求,再加上现在组件维护不及时,所以就导致现在网上流行说的这些坑。例如 antd 下通过 ConfigProvider getPopupContainer 指定弹框渲染的范围,这样就解决了上面说的逃逸问题。
或者重写了子应用的 document.body.appendChild,让dialog挂载到子应用的节点中,如果有多应用保活,重写后的appendChild会导致其他子应用也挂载到重写的那个子应用中,导致其他子应用出现dialog消失的问题。
使用 postcss-selector-namespace
在子应用中,配置 postcss 插件,给子应用添加类前缀:
例如在vue.config.js中添加
const selectorNamespace = require("postcss-selector-namespace");
css: {
loaderOptions: {
postcss: {
postcssOptions: {
plugins: [
selectorNamespace({
namespace(css) {
console.log('================postcss==============');
console.log(css);
/* 无需添加的样式 */
if (css.includes("element-variables.scss")) return "";
return ".vue2-app";
}
})
]
}
}
}
},
或者在postcss.config.js中
module.exports = {
'plugins': {
// 为微前端模块添加样式前缀,避免与其他应用冲突
'postcss-selector-namespace': {
namespace() {
return '.vue2-app'
}
},
}
}
记得在子应用根节点中加入与之匹配的样式前缀。
还是会存在上面插入 body 中的样式没有成功的问题,需要特殊处理。
element-ui和element-plus样式影响问题
主应用用element-plus,子应用用element-ui,导致样式发生变形。由于element-ui和element-plus前缀相同、命名规则相同,但内部部分样式实现方式不同,从而导致使用element-ui的样式会被污染,页面样式混乱
解决方案是写一个webpack loader替换element-ui class前缀,写一个postcss plugin替换样式前缀。
npm i change-prefix-loader postcss-change-css-prefix -D
在vue.config.js中添加如下代码
chainWebpack(config){
config.module
.rule('change-prefix')
.test(/\.js$/)
.include.add(path.resolve(__dirname,'./node_modules/element-ui/lib'))
.end()
.use('change-prefix')
.loader('change-prefix-loader')
.options({
prefix:'el-',
replace: 'portal-'
})
},
3.新建postcss.config.js文件
const addCssPrefix = require('postcss-change-css-prefix')
module.exports = {
plugins: [
addCssPrefix({
prefix: 'el-',
replace: 'portal-',
}),
],
}