最近公司项目重构,这次使用了element UI。作为一个现代的软件项目,未来支持换肤,或者根据系统选项默认换肤这些需求都要考虑。 所以需求如下:
需求
- 用户可以在软件内切换色彩主题(功能需求)。
- 基于element的原始主题修改。
- 可以在Vue项目中使用sass 变量。
- 在js环境内根据主题获取颜色变量。
- 字体图标也可以根据主题切换颜色。
方案
查了一些文章,躺了一顿坑后,发现了一个解决方案:

- 在assets文件夹中新建一个文件夹,比如说 themes文件夹,用于存储主题文件。
- 创建需要修改的主题变量,defauit.scss文件样式如下:
/* 改变主题色变量 */
$--color-primary: #00B9AD;
$--color-success: #05AA65;
$--color-warning: #FF9227;
$--color-danger: #F64346;
$--color-info: #BABEC5;
$--color-text-primary: #202021;
$--color-text-secondary: #606266;
$--background-color-base: #F7F8FA;
// 与CSS原生变量映射
#app {
--Main:#00B9AD;
--test:#FF9227;
}
这里要注意,这边的这个#app是变量作用域,是指的 VUE的那个#app 节点,为了在VUE组件总使用变量名称,需要在这里进行映射。
** 注意:CSS默认的变量赋值,是空格敏感的,赋值的时候最好不要加空格!(有坑)
** 另外请注意,Element的有些地方获取色值只支持hex的,所以尽量也使用HEX,不要用RGB色号!(坑)
black.scss文件样式如下:
/* 改变主题色变量 */
$--color-primary: #000;
$--color-success: #05AA65;
$--color-warning: #FF9227;
$--color-danger: #F64346;
$--color-info: #BABEC5;
$--color-text-primary: #202021;
$--color-text-secondary: #606266;
$--background-color-base: #F7F8FA;
// 与CSS原生变量映射
#app {
--Main:#000;
--test:#FF9227;
}
- 样式索引 index.scss 文件如下:
/* 改变 icon 字体路径变量,必需 */
$--font-path: '~element-ui/lib/theme-chalk/fonts';
.defaultTheme {
@import './default.scss';
@import "~element-ui/packages/theme-chalk/src/index";
}
.blackTheme {
@import './black.scss';
@import "~element-ui/packages/theme-chalk/src/index";
}
把原始主题的配置和自己写好的配置一起放入
.defaultTheme{}类里面。
- 在vue的
vue.config.js里面配置loader信息(如果没有这个文件可以自己在根目录创建(vue2.6+以上)):
module.exports = {
css: {
loaderOptions: {
sass: {
prependData: `@import "@/assets/themes/index.scss";`
}
}
}
}
- 接下来就可以在VUE的组件中使用方法来改变主题啦。
data() {
return {
themesName: 'defaultTheme',
}
},
methods: {
themes() {
this.themesName = 'blackTheme'
document.body.className = this.themesName;
}
}
分析一下:
1.在data里面创建一个 themesName 的变量,存储默认的主题名。 2.传入一个主题名称,并把主题名添加到body的样式上
document.body.className = this.themesName;
这样就可以实现主题切换了! 但是问题来了,我的图标是使用font class 的,需要在控件的属性中传入颜色色号,才能实现颜色切换的,怎么办?
使用CSS默认的变量动态获取主题色号
这时候上面Sass文件中颜色映射量派上用场啦:
// black.scss
#app {
--Main:#000;
--test:#FF9227;
}
就是在#app节点下面创建了css变量 --Main:#000;,这个色号应该跟主题颜色保持一致
那么就可以使用原生的js获取到颜色啦:
getComputedStyle(document.getElementById('app')).getPropertyValue('--Main')
然后在项目初始化的时候,可以在生命周期的created函数上先获取这个节点的色号,存在data中,然后绑定到需要传入icon颜色的地方,就是可以实现切换icon色号啦。
created() {
document.body.className = this.themesName;
this.color = getComputedStyle(document.getElementById('app')).getPropertyValue('--Main')
}
需要注意的是,每次切换主题的时候,也要使用获取颜色变量的方式来更新一下色号。
themes() {
this.themesName = 'blackTheme'
document.body.className = this.themesName;
this.color = getComputedStyle(document.getElementById('app')).getPropertyValue('--Main')
}
在VUE 组件中使用主题变量
根据CSS官方的说明:color: var(--Main)这样来使用局部变量即可,并且由于已经在loader配置了全局的引入,所以这里面不需要在单独引入啦
<style lang="scss" scoped>
.main {
color: var(--Main)
}
</style>
OK,这下可以开心的切换颜色了有没有,并且还可以随时获取当前主题色的色号,麻麻再也不用担心我的SVG变色切换了有木有。
PS:吐槽一下Element交流群全是在问问题的,但是没有人在里面帮你解决问题,感觉太孤独了,这一点真的不如antd啊,那边的QQ群热心人士很多……而且文档质量更是……不说了😭