vue2.x element-ui自定义主题 适配css var 变量换肤,真正的自由切换皮肤

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

执行后不出意外的话,意外出现了,报错了。。。开始手忙脚乱, 怀疑是不是姿势不对,再来一次还是报错


image.png

在网上冲浪了亿会会, 发现是node版本太高导致的 我使用的是14版本,建议是 11.15以下,解决方案有以下两种(冲浪的收获)

 - 安装低版本 node  11.15以下
 - 安装 element-themex

我这里用的是第二种方案 安装 element-themex

image-1.png

安装成功后再次执行 node_modules/.bin/et -i

image-2.png

这次终于成功了,开心~ !我们可以在根目录下看到 scss 变量文件

image-3.png

第三步 修改变量文件 这里把默认色修改为css 变量

image-4.png
/* 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

天呐!意外再一次出现,报错了,天塌了!!!!

image-5.png

报错提示很明显,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

初始化完成如下图

image-7.png

我们可以打开 element-variables.scss看一下是否已经替换完成,且 mixToVar 函数是否注入

image-11.png

替换的文件会包含 node_modules/element-theme-chalk 里面的scss文件


image-8.png

再次修改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

这一次没有出现意外,成功了!!!!!!!!!趾高气昂!!!!

image-9.png

打开生成的主题文件夹theme下的css文件可以看到,替换好的 css var函数

image-10.png

第四步 获取变量,并生产主题设置工具

虽然颜色值已经替换完成,但是可以发现都是一些颜色和变量组成的新变量,无法阅读,也没法在外面设置,所以工具的另外一个作用就是生成一个工具函数,供外部项目使用

执行ele-theme-tool 提供的命令


node_modules/.bin/ett -s

这个命令其实还有设置默认颜色(#409EFF)的功能,颜色设置没有暴露出来,有兴趣的自己实现下

执行完后可以看到css 文件已经修改

image-12.png

项目引入编译好的主题


import "../theme/index.css";

运行项目可以发现主题已经修改为默认的主题色 #409EFF

image-13.png

那如何动态修改呢?执行完命令后,项目根路径下多了一个 theme-tool-web.js 文件, 在项目中使用它

image-15.png

刷新页面可以看到,颜色值已经修改为设置的颜色

image-14.png

使用 ColorPicker 颜色选择器,直接切换颜色,也可以正常执行

image-16.png
如果使用中遇到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

总结:可以愉快的自定义主题了,如果需要兼容低版本浏览器,建议考虑其他方案或者使用css-vars-ponyfill适配

头发没了。。。,有更好的方案,可以再评论区分享下。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,772评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,458评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,610评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,640评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,657评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,590评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,962评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,631评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,870评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,611评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,704评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,386评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,969评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,944评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,179评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,742评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,440评论 2 342

推荐阅读更多精彩内容