qiankun微前端 —— 样式隔离

2,585 阅读3分钟

微前端的选型,暂不考虑,qiankun作为微前端的一种融合方式,目前也比较成熟,但是由于各类开发技术选型非常繁多,导致了在项目中配置不同,解决别人的问题,不一定能解决自己的问题。例如:

  • 使用的js框架的不同或版本的不同:vue/react 等,vue2/vue3 等
  • ui组件库版本的不同,以element为例,大版本上可分为element-ui/element-plus,小版本就更多了。
  • 打包工具的不同,比如常用的 webpack/vite等
  • 各类插件版本的不同,比如,node的版本,webpack的版本,vite的版本,node-sass/sass-loader的版本等

本文仅记录项目开发过程中遇到的一些坑,和如何解决的。希望能在你迷茫的时候有所帮助。同时也呼吁小伙伴们,也能养成记录的习惯。 宁滥毋缺。好了,说了很多废话,下面回归正题。

场景一 主应用和子应用使用了不同版本的组件库

1. 版本库

1. 主应用依赖包版本
"dependencies": {
    "axios": "^1.3.2",
    "element-plus": "^2.2.30",
    "qiankun": "^2.10.2",
    "vue": "^3.2.13",
  },
  "devDependencies": {
    "postcss": "^7.0.0",
    "postcss-change-css-prefix": "^1.0.4",
    "postcss-loader": "^7.3.0",
    "sass": "^1.32.7",
    "sass-loader": "^12.0.0",
    "sass-resources-loader": "^2.2.5",
    "typescript": "~4.5.5",
    "webpack-dev-server": "^4.0.0-beta.0"
  }
  
2. 子应用依赖包版本  
// todo

2. 基于qinkun的样式隔离

qiankun针对样式隔离主要通过沙箱的方式,参考官方api说明。

image.png

通过这种方式,在不引用组件库的情况下,看似很完美,实则也挑不出什么毛病,但是引用了组件库后。组件库有些组件是通过jsx语法生成并插入到body节点后,例如(Popover、内置了popover的select/datapicker等组件)便会出现主应用全局样式会影响到子应用。

3. 组件库的命名空间

为了解决主应用对子应用的样式覆盖和影响,组件库引入命名空间。下面提供了webpack 和 vite的两种配置方式,官网配置提供了vite的完整配置。针对webpack的配置提供了一种思路,需要自己查阅文档进行配置,下面提供完整配置

  1. 添加全局配置:namespace

增加了主应用的命名空间,替换了所有的组件样式,但是通过js生成的组件,例如:el-message、el-message-box、el-loading,没有生效,需要在引入element-plus时,配置namespace,参考 github.com/element-plu…

<!-- main.ts -->

import App from './App.vue';
import ElementPlus from 'element-plus';

const app = createApp(App);

app.use(ElementPlus, {
  locale: zhCn,
  namespace: 'main-el'
})
  1. 使用 ElConfigProvider 包装您的根组件。
<!-- App.vue -->
<template>
  <el-config-provider namespace="ep">
    <!-- ... -->
  </el-config-provider>
</template>
  1. 设置 SCSS 和 CSS 变量 创建 styles/element/index.scss
// styles/element/index.scss
// we can add this to custom namespace, default is 'el'
@forward 'element-plus/theme-chalk/src/mixins/config.scss' with (
  $namespace: 'ep'
);
// 配置主题色,根据项目需要。
@forward 'element-plus/theme-chalk/src/common/var.scss' with (
 $colors: (
    'primary': (
      'base': #009982,
    ),
  ),
);

注意:引入路径不同,对应不同的配置,这里我还配置了主题色#009982,forward的一个变量地址。

  1. vite构建方式,需要修改vite.config.ts文件,在 vite.config.ts 中导入 styles/element/index.scss
import { defineConfig } from 'vite'
// https://vitejs.dev/config/
export default defineConfig({
  // ...
  css: {
    preprocessorOptions: {
      scss: {
        additionalData: `@use "~/styles/element/index.scss" as *;`,
      },
    },
  },
  // ...
})
  1. webpack构建方式,需要修改vue.config.js文件,在 vue.config.js 中导入 styles/element/index.scss
const { defineConfig } = require('@vue/cli-service');

// https://cli.vuejs.org/zh/config/#css-modules
module.exports = defineConfig({
  // ...
  css: {
    loaderOptions: {
      sass: {
        additionalData: `@import "./src/styles/element/index.scss";`,
        // Note: this option is named as "prependData" in sass-loader v8
        // prependData: `@import "./src/styles/element/index.scss";`,
      },
    },
  },
  // ...
})

注意:

  1. 根据sass-lodader的版本,需要选择不同的属性additionalDataprependData
  2. 根据sass-lodader的版本,需要选择不同的引入方式(@import/@use
  3. 根据预编译处理器选择对应的sass或者scss配置

4. 主应用全局组件的样式添加

<!-- main.ts -->

import App from './App.vue';
import ElementPlus from 'element-plus';

const app = createApp(App);

app.use(ElementPlus, {
  locale: zhCn,
  namespace: 'main-el'
})

场景二 主应用和子应用使用相同版本的组件库

// todo

参考文档

1. qiankun官网
2. element-plus 自定义命名空间
3. vue Cli 配置文档
4. vite配置(官网)
5. webpack配置(官网)