B端业务中有个需求,不同的客户需要有自己的主题色, element-ui 官网提供的换肤方式不适用与用户自定义切换颜色(别问我为什么不升级,你懂得!),用户能切换的是内置好的,因为客户很多,不可能内置这么多主题。所以决定采用css变量来实现。说干就干~
1. 根据element-ui官网提供的方案(项目使用的是less,所以使用ele提供的主题工具)
第一步 安装主题工具和主题(推荐安装在当前项目下)
node环境 14.20.0
npm i element-theme element-theme-chalk -D
第二步 初始化变量文件
node_modules/.bin/et -i
执行后不出意外的话,意外出现了,报错了。。。开始手忙脚乱, 怀疑是不是姿势不对,再来一次还是报错
在网上冲浪了亿会会, 发现是node版本太高导致的 我使用的是14版本,建议是 11.15以下,解决方案有以下两种(冲浪的收获)
- 安装低版本 node 11.15以下
- 安装 element-themex
我这里用的是第二种方案 安装 element-themex
安装成功后再次执行 node_modules/.bin/et -i
这次终于成功了,开心~ !我们可以在根目录下看到 scss 变量文件
第三步 修改变量文件 这里把默认色修改为css 变量
/* Color
-------------------------- */
/// color|1|Brand Color|0
$--color-primary: var(--customize-theme) !default;
/// color|1|Background Color|4
$--color-white: #FFFFFF !default;
/// color|1|Background Color|4
$--color-black: #000000 !default;
第四步 执行编译主题命令(坐等主题生成 嗯哼!)
node_modules/.bin/et
天呐!意外再一次出现,报错了,天塌了!!!!
报错提示很明显,scss mix函数的入参需要是一个颜色值。我们传入一个 css 变量它是不支持的。因为ele主题中一些样式使用的是默认颜色与另外一种混合,根据一定的比例混合在一起,生成另一种颜色。于是网上冲浪~~无果!!!
没办法业务需求要实现,头发还是需要掉。
经过深思熟虑决定解决这个报错,手动改源码是不可能的,因为颜色是scss编译过程生成的,直接改源码的事就不想聊了~
2.修改 scss 变量的mix函数
第一步 实现 mixToVar
函数
其实ele提供的主题编译工具逻辑很简单,就是把 element-theme-chalk
中的变量暴露出来,供项目修改,修改后再执行编译命令,把修改后的主题文件重写进 element-theme-chalk
中的变量文件,在执行scss编译,把scss文件编译成css文件
那我们也可以加一层命令,就是把 mix
函数替换掉,替换逻辑很简单,读取element-theme-chalk
中的scss文件,使用字符串替换就行,函数名为 mixToVar
mixToVar
入参与 mix
函数一致,这里不用去关心 mix
函数的内部实现
mixToVar
函数的需要实现的功能如下
- 判断入参是否存在css var 函数(可以通过scss字符串提供的方法, str-index())
- 存在var函数字符串则获取css变量名 和 其他参数进行拼接 返回新的 var() 函数
- 参数中不存在 var函数 则直接返回
mix
函数
mixToVar
具体实现如下 主要使用字符串相关的操作 判断索引、截取等
/* 自定义函数 */
@function mixToVar($c1, $c2, $percent: 50%) {
$str: '';
$len: str-index($percent + '', '%');
$percentNew: $percent;
@if $len {
$percentNew: str-slice($percent+'', 0, $len - 1)
}
$c1Str: $c1+'';
$c2Str: $c2+'';
$c1VarIdx: str-index($c1Str, 'var(');
$c2VarIdx: str-index($c2Str, 'var(');
@if $c1VarIdx {
$str: '#{str-slice($c1Str, str-index($c1Str, '(') + 1, str-index($c1Str, ')') - 1)}'
}
@else {
$str: '--YS#{transformColor($c1)}'
}
@if $c2VarIdx {
$c2Var: #{str-slice($c2Str,str-index($c2Str, '(') + 1, str-index($c2Str, ')') - 1)};
$str: $str + '_#{$c2Var}_' + $percentNew;
@return var(unquote($str));
}
@if $c1VarIdx {
$str: $str + '_--YS#{transformColor($c2)}_' + $percentNew;
@return var(unquote($str));
}
@return mix($c1, $c2, $percent);
}
@function transformColor($color) {
$str: $color+'';
$idx: str-index($str, '#');
@if $idx {
@return str-slice($str, $idx+ 1);
}
@return $color;
}
// YS 表示颜色值 后续容易分割字符串
// _ 后续分割字符使用
// mixToVar 返回示例
// 1
// mixToVar(#409EFF, var(--customize-theme), 60%)
// 返回结果 var(--YS409EFF_--customize-theme_60)
// 2
// mixToVar(var(--customize-theme), #409EFF, 60%)
// 返回结果 var(--customize-theme_--YS409EFF_60)
// 3
// mixToVar(var(--customize-theme), var(--customize-theme1), 60%)
// 返回结果 var(--customize-theme_--customize-theme1_60)
// 4
// mixToVar(#FFFFFF, #409EFF, 10%)
// 返回结果 #53a8ff
第二步 实现主题工具
函数已经写好了,后面就是替换mix
函数, 为了和ele风格保持一致,我这里也简单实现一个命令行工具(需要一点node基础)ele-theme-tool
,工具主要是转换 scss 文件
工具主要功能有下面几点:
- 替换
mix
函数同时, 注入写好的mixToVar
函数 - 生成当前项目需要用的 主题设置函数
- 主题设置函数根据变量和颜色和比例生成新的颜色
因为变量是动态生成的变量,项目是无感的,项目不知道工具生成了那些变量,为了傻瓜式,主题工具生成好js方法
工具写好后发布,然后安装工具(项目使用less,scss都可以使用它生成element-ui
主题)
npm i ele-theme-tool -D
安装成功后执行以下命令进行初始化
执行前请确保 element-ui 主题色没有被定制过,如果定制过重新安装 element-theme-chalk 再执行
如果当前根目录下已经存在
element-variables.scss
先删除再执行
node_modules/.bin/ett -i
初始化完成如下图
我们可以打开 element-variables.scss
看一下是否已经替换完成,且 mixToVar
函数是否注入
替换的文件会包含 node_modules/element-theme-chalk 里面的scss文件
再次修改element-variables.scss
文件
/* Color
-------------------------- */
/// color|1|Brand Color|0
$--color-primary: var(--customize-theme) !default;
/// color|1|Background Color|4
$--color-white: #FFFFFF !default;
/// color|1|Background Color|4
$--color-black: #000000 !default;
第三步 执行element-ui提供的 主题编译方法 注意是 element-ui
提供的方法
node_modules/.bin/et
这一次没有出现意外,成功了!!!!!!!!!趾高气昂!!!!
打开生成的主题文件夹theme
下的css文件可以看到,替换好的 css var函数
第四步 获取变量,并生产主题设置工具
虽然颜色值已经替换完成,但是可以发现都是一些颜色和变量组成的新变量,无法阅读,也没法在外面设置,所以工具的另外一个作用就是生成一个工具函数,供外部项目使用
执行ele-theme-tool
提供的命令
node_modules/.bin/ett -s
这个命令其实还有设置默认颜色(#409EFF)的功能,颜色设置没有暴露出来,有兴趣的自己实现下
执行完后可以看到css 文件已经修改
项目引入编译好的主题
import "../theme/index.css";
运行项目可以发现主题已经修改为默认的主题色 #409EFF
那如何动态修改呢?执行完命令后,项目根路径下多了一个 theme-tool-web.js
文件, 在项目中使用它
刷新页面可以看到,颜色值已经修改为设置的颜色
使用 ColorPicker 颜色选择器,直接切换颜色,也可以正常执行
如果使用中遇到node版本等问题可以初始化一个新的空项目,使用指定的node版本,定义完主题后,把主题文件夹和theme-tool-web.js 文件复制到实际项目中引用(element-ui 提供的自定义工具,其实就是生成了一个 css 目录)
ele-theme-tool
也可以直接通过js调用方式工作
例如根据element-ui
二次封装的组件库可以使用此方式换肤, 这里就不在赘述,有需求,可以根据源码改造、封装
const { init, build, varUtil } = require('ele-theme-tool')
init.themeInit({
targetThemePathRoot,
themeDir,
themeVarName,
themeVarPath,
message: '初始化主题变量'
})
build.themeBuild({
targetThemePathRoot,
targetFontsRoot,
themeDir,
targetThemeVarPath,
minimize: true,
out: themeOutDir,
themeVarPath: path.resolve('variables.scss')
},() => {
varUtil.setDefaultVarColor({
themeDir:themeOutDir
})
})
github地址: ele-theme-tool