微前端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-',
    }),
  ],
}
相关推荐
Myli_ing1 分钟前
考研倒计时-配色+1
前端·javascript·考研
余道各努力,千里自同风4 分钟前
前端 vue 如何区分开发环境
前端·javascript·vue.js
软件小伟13 分钟前
Vue3+element-plus 实现中英文切换(Vue-i18n组件的使用)
前端·javascript·vue.js
醉の虾34 分钟前
Vue3 使用v-for 渲染列表数据后更新
前端·javascript·vue.js
张小小大智慧43 分钟前
TypeScript 的发展与基本语法
前端·javascript·typescript
hummhumm1 小时前
第 22 章 - Go语言 测试与基准测试
java·大数据·开发语言·前端·python·golang·log4j
asleep7011 小时前
第8章利用CSS制作导航菜单
前端·css
hummhumm1 小时前
第 28 章 - Go语言 Web 开发入门
java·开发语言·前端·python·sql·golang·前端框架
幼儿园的小霸王2 小时前
通过socket设置版本更新提示
前端·vue.js·webpack·typescript·前端框架·anti-design-vue
疯狂的沙粒2 小时前
对 TypeScript 中高级类型的理解?应该在哪些方面可以更好的使用!
前端·javascript·typescript