前段时间我在公司落地了一套微前端的架构方案并推向了生产环境,也写过一篇文章来总结归纳这个过程踩过的坑,有兴趣的朋友可以点击下方链接进行仔细阅读,建议是可以阅读完后实践一遍,这样才算是掌握了。
这篇文章当中我也有写我是怎么解决样式冲突问题的,但是我觉得那个方案并不完美,有点像是打补丁一样,并没有从根源上解决,这段时间我也一直在寻找更完美的解决方案,今天它来了!!!
其实
qiankun内部有个属性是来控制样式分离的(experimentalStyleIsolation),使用过了效果并不明显,但这个属性还是在实验阶段,期待官方也能给出一个完美的解决方案。
方案探索过程
就当前遇到的问题我们可以定位到,只有主应用和子应用之前才可能造成样式冲突,并且是用了同一UI组件库或者是class同名造成的,子应用与子应用之前是不会发生的,沿着这个线索,我们是不是可以大胆的假设,如果主应用没有样式,那么是不是就不会产生样式冲突。但是主应用要完全没有样式也是不现实的,在实际的微前端落地场景中,一般都是把管理系统的头部状态栏和菜单栏放在主应用,具体内容放在子应用。对于这个问题我还特地问题别的公司的同学,的确有公司是这么做的,就是主应用自己手写样式,子应用正常使用组件库开发,也是一个解决方案之前一。但是我今天要给出另外一个更完美的解决方案。
组件库自定义命名空间
经过对element-plus一番研究,它开放了一个自定义命名空间的能力,什么意思?说白了就是可以自己定义el为其他自己想要定义的字符串,这样可以达到即使主子应用使用了同一个UI组件库,它也不会产生样式冲突,因为它class都不一样,下面直接上配置代码。
element-plus官方其实给了配置步骤,但是还有的点没写来,导致配置完没效果,要不然也没有写这篇文章的必要!
element-plus.gitee.io/zh-CN/guide…
Vite搭建的项目如何配置
- 使用
ElConfigProvider包装您的根组件。
<!-- App.vue -->
<template>
<el-config-provider namespace="ep">
<!-- ... -->
</el-config-provider>
</template>
- 创建
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'
);
// ...
- vite.config.ts配置
import { defineConfig } from 'vite'
// https://vitejs.dev/config/
export default defineConfig({
// ...
css: {
preprocessorOptions: {
scss: {
additionalData: `@use "~/styles/element/index.scss" as *;`,
},
},
},
// ...
})
so easy?? 这样搞没效果,咋整,又没有有效的错误提示,头大,这就是文档不清楚带来的问题。
经过一番研究才知道是引用element-plus的方式出了问题。你是不是也是这么引用的?
# main.ts
import { createApp } from 'vue'
import App from '@/App.vue'
import '@/assets/style/common.scss'
import ElementPlus form 'element-plus'
import 'element-plus/dist/index.scss'
import router from '@/router/index'
import store from '@/store/index'
createApp(App).use(router).use(store).use(ElementPlus).mount('#app')
是因为这么全量引入,那个修改 el 为 ep 的逻辑就没有产生作用,所以就没有效果,那我们可以采用按需引入的方式,借助Vite/webpack的能力来让那段修改命名空间的逻辑产生作用。
依然是按照官方的按需引入的方式来配置: element-plus.gitee.io/zh-CN/guide…
自动导入推荐
首先你需要安装unplugin-vue-components 和 unplugin-auto-import这两款插件
npm install -D unplugin-vue-components unplugin-auto-import
然后把下列代码插入到你的 Vite 的配置文件中
// vite.config.ts
import { defineConfig } from 'vite'
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
export default defineConfig({
// ...
plugins: [
// ...
AutoImport({
resolvers: [ElementPlusResolver()],
}),
Components({
resolvers: [ElementPlusResolver({ importStyle: 'sass' })],
}),
],
css: {
preprocessorOptions: {
scss: {
additionalData: `@use "@/assets/style/index.scss" as *;`
}
}
},
})
到这里Vite的项目配置完成了。
如果是vue-cli搭建的项目,vue.config.ts的配置有点不一样,当然大部分是相同的,我这里也补充完整!
// vue.config.ts
const { defineConfig } = require('@vue/cli-service');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const path = require('path');
const AutoImport = require('unplugin-auto-import/webpack');
const Components = require('unplugin-vue-components/webpack');
const { ElementPlusResolver } = require('unplugin-vue-components/resolvers');
module.exports = defineConfig({
transpileDependencies: true,
// 主要是这段css不一样
css: {
loaderOptions: {
scss: {
additionalData: '@use \'~@/theme/index.scss\';',
},
},
},
publicPath: process.env.NODE_ENV === 'production' ? '/vue-template' : '/',
configureWebpack: {
resolve: {
extensions: ['.js', '.vue', '.json', '.ts', '.tsx'],
alias: {
'@': path.resolve(__dirname, 'src/'),
},
},
plugins: [
AutoImport({
resolvers: [ElementPlusResolver()],
}),
Components({
resolvers: [ElementPlusResolver({ importStyle: 'sass' })],
}),
new MiniCssExtractPlugin({
// 修改打包后css文件名
filename: 'css/[name].[contenthash:8].css',
chunkFilename: 'css/[name].[contenthash:8].css',
}),
],
},
devServer: {
historyApiFallback: true,
proxy: {
'/api': {
target: 'http://localhost:8888',
changeOrigin: true,
pathRewrite: {
'^/api': '',
},
},
},
},
});
注意:要调试尽量是新搭一个空白的项目来配置调试,否则按照上面的代码来配置都有可能不生效,我上面的代码是经过实战检验的,遇到问题优先考虑项目本身代码的问题。
写在后面
其实很多解决问题的方案都是不断完善的一个过程,要不厌其烦的去找到更好的替代方案,对于个人成长是相当有好处的。