Element UI 主题切换实践(2020)

8,507 阅读4分钟

最近公司项目重构,这次使用了element UI。作为一个现代的软件项目,未来支持换肤,或者根据系统选项默认换肤这些需求都要考虑。 所以需求如下:

需求

  1. 用户可以在软件内切换色彩主题(功能需求)。
  2. 基于element的原始主题修改。
  3. 可以在Vue项目中使用sass 变量。
  4. 在js环境内根据主题获取颜色变量。
  5. 字体图标也可以根据主题切换颜色。

方案

查了一些文章,躺了一顿坑后,发现了一个解决方案:

  1. 在assets文件夹中新建一个文件夹,比如说 themes文件夹,用于存储主题文件。
  2. 创建需要修改的主题变量,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;
}
  1. 样式索引 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{}类里面。

  1. 在vue的 vue.config.js里面配置loader信息(如果没有这个文件可以自己在根目录创建(vue2.6+以上)):
module.exports = {
  css: {
    loaderOptions: {
      sass: {
        prependData: `@import "@/assets/themes/index.scss";`
      }
    }
  }
}
  1. 接下来就可以在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群热心人士很多……而且文档质量更是……不说了😭