微前端qiankun样式隔离总结

项目背景

主应用 vue2 + element;

子应用 vue3 + element-plus;

问题描述

主应用使用element而在子应用中使用element-plus,导致出现两个问题:

  1. 子应用中element-plus的样式和主应用中element的样式同名,导致被样式覆盖
  2. 由于像对话框等组件都是默认放在body下面的,所以会被放在主应用的body下而不是子应用的body下导致样式出现偏差

问题一场景复现

主应用触发

子应用触发

问题二场景复现

子应用的组件绑定到了主应用的body中

针对问题一的解决思考

使用严格沙箱

使用

在加载子应用时,添加strictStyleIsolation: true属性,实现形式为将整个子应用放到Shadow DOM内进行嵌入,实现主子应用完全隔离

js 复制代码
start({
  sandbox: {
    strictStyleIsolation: true,
  },
})

问题

vue3项目根本无法正常运行

问下chatGPT吧

既然如此就下发解决一下吧

js 复制代码
// 主
<div id="subapp-viewport"></div>

const apps = microApps.map(item => {
  return {
    ...item,
    container: '#subapp-viewport', // 子应用挂载的div
    props: {
      routerBase: item.activeRule, // 下发基础路由
      getGlobalState: store.getGlobalState // 下发getGlobalState方法
    }
  }
})

// 子
function render(props){
    const { container } = props
    app = createApp(App)
    app.use(store)
    app.use(router)
    debugger
    app.mount(container ? container.querySelector('#app') : '#app')
}

但是子应用的弹窗、抽屉、popover因找不到主应用的body会丢失,或跑到整个屏幕外

原因:element-plus 的全局变量是通过 :root 选择器来作用到根节点的,但是在 shadow dom 中,是无法通过 :root 来选中根节点的,也就导致了这部分的样式失效 解决:在 shadow dom 中也是存在根节点的,这个根节点名为 shadow host ,我们可以用 :host 来代替 :root 选中 shadow dom 对应的根节点 方案:写一个 loader,在 webpack 打包文件的时候把 :root 替换成 :host

js 复制代码
const replaceStyleLoader = function (source) { 
    return source.replace(/:root/g, ':host') 
} 
module.exports = myStyleLoader
js 复制代码
module: {
  rules: [
    {
      test: /\.scss$/,
      include: [
        path.resolve(__dirname, 'node_modules/element-plus/theme-chalk') 
      ],
      use: [
        'style-loader',
        'css-loader',
        'replace-style-loader',
        'sass-loader'
      ]
    }
  ]
}

还有一个无法解决的问题:子应用的弹窗、抽屉、popover因找不到主应用的body会丢失或绑定到主应用的body(绑定到body可以理解,丢失真的神奇不过就复现了一两次所以以后在研究吧...)

结论:放弃这种方式,太麻烦而且主应用不方便去修改子应用的样式,还有就是对react不友好

使用实验性沙箱

在加载子应用时,添加experimentalStyleIsolation: true属性,实现形式类似于vue中style标签中的scoped属性,qiankun会自动为子应用所有的样式增加后缀标签,如:div[data-qiankun-microName]

js 复制代码
start({
  sandbox: {
    experimentalStyleIsolation: true,
  },
})

也可以解决样式被覆盖的问题但同样无法解决子应用的弹窗、抽屉、popover因为绑定到主应用的body上而导致使用的是主应用的样式的问题

结论:放弃这种方式,无法解决问题二

针对问题二的思考

既然都无法解决问题二,第一个想到的点是实现元素隔离,但是qiankun并不支持该功能,重写的代价太大,所以就只能退而求其次将子应用的样式和class的el-前缀进行转换

思路一

使用element-plus的全局配置修改class和组件的名字(感觉就是主题色替换的思路),但是这里有个问题就是虽然class和组件的名字改变了但图标等个别组件是会通过已经打包好的样式引入的,所以还需要使用postCss插件替换一遍(注意:关闭qiankun的样式沙箱

js 复制代码
// app.vue
<el-config-provider :locale="local" namspace="subel">
<router-view />
</el-config-provider>
  
// 新建style/element/index.scss
// element前缀样式修改,el改为subel
@forward 'element-plus/theme-chalk/src/mixins/config.scss' with (
  $namespace: 'subel'
);
    
// vue.config.js
module:{
  rules:[
    {
      test: /\.(scss|sass)$/,
      exclude: resolve('src/style/element/index.scss'), 
      include: path.resolve(__dirname, './node_modules/element-plus/'),
      use: [
        {
          loader: "sass-loader",
          options: {
            implementation: require("sass"), 
            prependData: `@use "@/style/element/index.scss" as *;`,
          }
        }
      ]
    }
  ]
}  

// postcss.config.js
const addCssPrefix = require('postcss-change-css-prefix-namespace')
module.exports = {
  plugins: [
    'postcss-preset-env',
    addCssPrefix({
      prefix: 'el-',
      replace: 'subel-',
    }),
  ],
}

思路二(目前感觉最好的思路了...)

既然都需要postCss转换那我直接转了不就得了。。。。

js 复制代码
// app.vue
<el-config-provider :locale="local" namspace="subel">
<router-view />
</el-config-provider>

// postcss.config.js
const addCssPrefix = require('postcss-change-css-prefix-namespace')
module.exports = {
  plugins: [
    'postcss-preset-env',
    addCssPrefix({
      prefix: 'el-',
      replace: 'subel-',
    }),
  ],
}
相关推荐
我要洋人死1 小时前
导航栏及下拉菜单的实现
前端·css·css3
科技探秘人1 小时前
Chrome与火狐哪个浏览器的隐私追踪功能更好
前端·chrome
科技探秘人1 小时前
Chrome与傲游浏览器性能与功能的深度对比
前端·chrome
JerryXZR1 小时前
前端开发中ES6的技术细节二
前端·javascript·es6
七星静香1 小时前
laravel chunkById 分块查询 使用时的问题
java·前端·laravel
q2498596931 小时前
前端预览word、excel、ppt
前端·word·excel
小华同学ai2 小时前
wflow-web:开源啦 ,高仿钉钉、飞书、企业微信的审批流程设计器,轻松打造属于你的工作流设计器
前端·钉钉·飞书
Gavin_9152 小时前
【JavaScript】模块化开发
前端·javascript·vue.js
懒大王爱吃狼3 小时前
Python教程:python枚举类定义和使用
开发语言·前端·javascript·python·python基础·python编程·python书籍
逐·風7 小时前
unity关于自定义渲染、内存管理、性能调优、复杂物理模拟、并行计算以及插件开发
前端·unity·c#