element换肤及相关源码记录
date
Jan 13, 2023
slug
element-plus
status
Published
tags
ElementPlus
summary
ElementPlus Skin Peeler
type
Post
element class采用BEM的命名思想
bem即block-element-modify,B意思是Block 块,E意思是Element 元素,M意思是Modifier 修饰器,就是将命名按照层级进行命名。
每一个块(block)名应该有一个命名空间(前缀)
block
代表了更高级别的抽象或组件。
block__element
代表 .block 的后代,用于形成一个完整的 .block 的整体。
block--modifier
代表 .block 的不同状态或不同版本。 使用两个连字符和下划线而不是一个,是为了让你自己的块可以用单个连字符来界定。如:
.sub-block__element {}
.sub-block--modifier {}
使用BEM命名规则的好处:从其名字可以知道某个标记的含义,通过查看 HTML 代码中的 class 属性,就能知道元素之间的关联。(在明确关联性模块关系时使用)
scss基本使用
SCSS的优势在于开发效率高。对于一些样式复杂的站点,用 SASS 之类的工具生成代码比手写 CSS 快得多
注释
/*
aaa
bbb
编译成css后依然存在
*/
//ccc
编译成css后消失
变量
以$符号开头,赋值方法与 CSS 属性的写法一样。
$color //表示一个变量(以$开头)
例:定义背景颜色
$color:#f3e1e1;
body{
$color1:red;
background-color:$color;
color:$color1;
}
编译成css之后:
body{ background-color:#f3e1e1; color:red;}
不能跨作用域使用变量
错误实例:(编译错误),若想编译成功需要在red后加上!global
$color:#f3e1e1;
body{
$color1:red;
background-color:$color;
color:$color1;
}
#app{
color:$color1;
}
支持控制语句
if
$color:5;
p{
@if $color==0{
color:blue;
}@else if $color ==1{
color:red;
}@else{
color:yellow;
}
}
for
循环语句
表达式:
@for $var from <start> through <end>
或 @for $var from <start> to <end>
through 和 to 的相同点与不同点:
- 相同点:两者均包含
的值
- 不同点:through包含
的值,但to不包含的值
@for $i from 1 through 3 {
.item-#{$i} { width: 2em * $i; }
}
/*compile:*/
.item-1 {
width: 2em;
}
.item-2 {
width: 4em;
}
.item-3 {
width: 6em;
}
each
循环语句:查询每一个
表达式:
$var in $vars
$var
可以是任何变量名$vars
只能是Lists
或者Maps
遍历循环拿到颜色索引值,i接收变量的索引值,使用index查找cr在$index里的索引值
.cr{
width:200px;
height:200px;
display:inline-block;
}
$cr_list:{
#f3a5bd,#8b80b8,##00326,
#f2711c,#db2828,#42b8dd,
#f3a5bd,#8b80b8,##00326,
}
@each $cr in $cr_list{
$i:index($cr_list,$cr);
.cr:nth-child(#{$i}){
background:$cr;
}
}

一维数组:
@each $animal in puma, sea-slug, egret, salamander {
.#{$animal}-icon {
background-image: url('/images/#{$animal}.png');
}
}
// compile:
.puma-icon {
background-image: url('/images/puma.png'); }
.sea-slug-icon {
background-image: url('/images/sea-slug.png'); }
.egret-icon {
background-image: url('/images/egret.png'); }
.salamander-icon {
background-image: url('/images/salamander.png'); }
二维数组:
@each $animal, $color, $cursor in (puma, black, default),
(sea-slug, blue, pointer),
(egret, white, move) {
.#{$animal}-icon {
background-image: url('/images/#{$animal}.png');
border: 2px solid $color;
cursor: $cursor;
}
}
/* compile:*/
.puma-icon {
background-image: url('/images/puma.png');
border: 2px solid black;
cursor: default; }
.sea-slug-icon {
background-image: url('/images/sea-slug.png');
border: 2px solid blue;
cursor: pointer; }
.egret-icon {
background-image: url('/images/egret.png');
border: 2px solid white;
cursor: move; }
maps:
@each $header, $size in (h1: 2em, h2: 1.5em, h3: 1.2em) {
#{$header} {
font-size: $size;
}
}
/* compile:*/
h1 {
font-size: 2em; }
h2 {
font-size: 1.5em; }
h3 {
font-size: 1.2em; }
ElementPlus包分析
包目录作用
根目录下存放有
dist es lib node_modules theme-chalk
文件夹- dist 最终构建的完整产物
- lib文件夹存放着ElementPlus打包后的文件,
CommonJS
格式(动态加载语句)
- es文件夹存放着ElementPlus打包后的文件,
ES
格式(es-module打包方式,静态不可以动态加载语句) - 这两个模块解决的问题
- 解决变量污染问题,每个文件都是独立的作用域,所以不存在变量污染
- 解决代码维护问题,一个文件里代码非常清晰
- 解决文件依赖问题,一个文件里可以清楚的看到依赖了那些其它文件
- node_modules文件夹主要存放了组件相关的源代码
- src文件夹下存放了像指令、混入、工具方法等源代码
- theme-chalk存放组件库样式,可理解为样式产物(
gulp
打包,监视文件的修改) - 样式文件和字体图标的打包使用的是
/theme-chalk/gulpfile.js
, 把每个scss
文件打包成单独的css
, 其中包含了通用的 base 样式,还有每个组件的样式文件。 dark
目录src
目录
css-vars.css
定义.dark变量类名以及主题色基础配置。即设置本套主题色为dark前缀,当html
标签中设置class=“dark”此主题生效
|-- common // 公共样式
|-- popup.scss // 定义弹出组件的样式
|-- transition.scss // 定义Element css动画样式
|-- var.scss // 定义变量
|-- dark
|-- var.scss //定义element-plus中黑暗色主题基础变量
|-- css-var.scss //更改element-plus原始的var变量
|-- select
|-- common.scss //响应式namespace命名空间
|-- date-picker // 定义时间日期选择样式
|-- fonts //icon 字体
|-- mixins // 混合样式 包含一些全局的函数 等
|-- _button.scss // button 组件样式
|-- config.scss // 配置文件 例如 样式库前缀名配置
|-- function.scss // 全局的样式函数
|-- mixins.scss // 全局的样式混合定义
|-- utils.scss // 工具样式混合定义
|-- alert.scss // 单独 Alert 组件 的样式定义
...
|-- index.scss // Element 库 全部的组件样式文件
/theme-chalk/src/dark/css-vars.scss
获取element-plus原始的var变量进行重写

/theme-chalk/src/common/var.scss
@mixin set-color-mix-level(
$type,
$number,
$mode: 'light',
$mix-color: $color-white
) {
$colors: map.deep-merge(
(
$type: (
'#{$mode}-#{$number}':
mix(
$mix-color,
map.get($colors, $type, 'base'),
math.percentage(math.div($number, 10))
),
),
),
$colors
) !global;
}
@each $type in $types {
@for $i from 1 through 9 {
@include set-color-mix-level($type, $i, 'light', $color-white);
}
}
此函数的作用为定义
$color-white$color
设置主色调mix函数将颜色从十六进制转化为rgb,同时根据两个变量为根设置:root选择器的样式覆盖node.style.setProperty(color,color-white)
相关源码解析
/theme-chalk/common/var.scss 组件相关颜色的抽取与定义
!global全局变量
变量 | 使用函数 | 作用 |
$type | @mixin set-color-mix-level | 设置黑白主题。定义 $color-white$color 设置主色调mix函数将颜色从十六进制转化为rgb,同时根据两个变量为根设置:root选择器的样式覆盖node.style.setProperty(color,color-white) |
$button-border-color | @each $type in $types | 对按钮的边框颜色进行分级 |
$button-bg-color | @each $type in $types | 对按钮的背景颜色进行分级 |
$link-text-color | @each $type in $types | 对链接文字的颜色进行颜色与类型的获取并分级 |
colors |map.deep-merge|将type中定义的各个状态(success、warning 等等)对应的颜色值map和原生color的map合并起来。 map.get($map,key) 方法用于像对象一样,根据键值获得map中对应的属性值。 |
!default默认变量
若变量已经赋值则不使用默认值(如果该变量已有值,则其旧值将被覆盖);如果没有赋值就使用默认值。在Scss提供的标志中,仅当变量未定义或其值为
null
时,才会将值分配给该变量。否则,将使用现有值。!default
$text-color | 对文本颜色进行常规悬浮禁用等状态时的定义 |
$border-color | 对四个边框颜色进行不同深浅颜色以及层级颜色的定义 |
$fill-color | 定义黑白层级并对其进行分级填充颜色 |
$bg-color | 定义背景色,分为默认状态以及覆盖状态 |
$border-width | 默认边框宽度 |
$border-style | 默认边框样式为实线 |
$border-color-hover | 默认鼠标悬浮于边框的颜色 |
$border-radius | 设置外边框圆角,为基础、小型、圆圈设置不同的大小 |
$box-shadow | 对卡片式的背景颜色进行light、lighter、dark等颜色样式的分级与定义更深或更浅颜色的阴影 |
$font-family | 定义多种字体类型和系列 |
$font-size | 定义字体的大小 |
$z-index | 分为三级,2000用于弹出框、悬浮框,1为普通级别,1000为顶部栏 |
$disabled | 设置禁用时的背景、文字、边框颜色 |
$common-component-size | 设置通用组件的三种大小:默认、小型、大型 |
$overlay-color | 定义遮罩层背景颜色样式 |
$mask-color | 遮罩层透明度 |
$checkbox | 多选框文字大小、输入框宽高、禁用状态和选择状态等基本属性 |
$checkbox-button | 多选框按钮背景、颜色和边框的颜色 |
$checkbox-bordered-padding-left | 定义左填充接壤的复选框左内边距宽度,分为默认、小和大三种距离大小 |
$checkbox-bordered-padding-right | 定义右填充接壤的复选框右内边距宽度 |
$radio | 单选框按钮形式的 Radio 激活时的文本颜色以及单选框尺寸 |
$radio-height | 单选框默认高度样式 |
$radio-button | 单选框默认按钮样式 |
$radio-disabled | 单选框禁用状态样式 |
$radio-checked | 单选框选择状态文本、输入框、字体图标等样式 |
$radio-bordered-input-height | 带边框单选按钮高度 |
$radio-bordered-input-width | 带边框单选按钮宽度 |
$select | 选择器基本样式 |
$select-option | 选项字体颜色、禁用状态颜色、高度、鼠标悬浮颜色变化和选中时颜色 |
$select-group | 想定分组时文字颜色、高度与字体大小 |
$select-dropdown | 下拉框样式 |
$select-tags-prefix-padding | 在选择标记前填充边距值 |
$alert | 弹出框边距、圆角、文字大小以及字体图标样式 |
$messagebox | 消息弹出框标题颜色、内容字体大小与颜色和边距等基本样式 |
$message | 消息提示边距与关闭信息样式 |
$notification | 消息通知基本样式 |
$input | 输入框基本样式 |
$input-disabled | 禁用输入框基本样式 |
$input-font-size | 输入框字体大小 |
$input-height | 输入框高度 |
$input-line-height | 输入框字体行高 |
$input-number-width | 输入框数字宽度 |
$input-padding-horizontal | 输入框水平边距 |
$cascader | 级联选择器基本样式 |
$button | 按钮悬浮、活跃与禁用状态 |
$button-border-width | 按钮边框宽度 |
$button-border-color | 按钮边框颜色 |
$button-bg-color | 按钮背景颜色 |
$button-text-color | 按钮文本颜色 |
$button-font-size | 按钮字体大小 |
$button-border-radius | 按钮圆角 |
$button-padding-vertical | 按钮垂直边距 |
$button-padding-horizontal | 按钮水平边距 |
$switch | 开关打开与关闭样式 |
$dialog | 对话框宽度、距顶部距离、背景颜色与字体位置等样式 |
$table | 表格边框行列间距tr、td等基本样式 |
$table-font-size | 表格字体大小 |
$table-padding | 表格边距 |
$table-cell-padding | 表格单元格边距 |
$pagination | 分页字体、选择按钮等样式 |
$popup | z-index为2000的消息弹出框 |
$popover | 虚拟元素触发气泡卡片 |
$popper | 根据基准DOM调整的弹出框 |
$skeleton | div骨架颜色 |
$tag | 标签颜色与圆角 |
$tag-height | 标签高度 |
$tag-padding | 标签内边距 |
$tag-icon-size | 标签字体图标 |
$tree | 树状图配置 |
$dropdown | 下拉菜单 |
$drawer | 抽屉式窗体大小 |
$badge | 徽章按钮和图标上的数字或状态标记 |
$card | 卡片标题、阴影等样式 |
$slider | 滑块 |
$menu | 菜单 |
$rate | 评分 |
$datepicker | 日期选择器 |
$date-editor | 日期时间选择器 |
$loading | 加载状态 |
$scrollbar | 滚动条 |
$carousel | 替换浏览器原生滚动条高度大小等 |
$collapse | 折叠面板手风琴模式 |
$transfer | 穿梭框基本样式 |
$timeline | 时间线及节点基本样式 |
$tabs | 标签标记与选择基本样式 |
$backtop | 回到顶部 |
$link | 链接大小等 |
$link-text-color | 链接文字颜色 |
$calendar | 日历大小鼠标悬浮等样式 |
$form | 典型表单基本内容 |
$avatar | 头像circle、square大小与圆角 |
$avatar-size | 头像尺寸 |
$empty | 空状态占位提示 |
$descriptions | 表述列表字段样式 |
$result | 对用户的操作结果或者异常状态反馈样式定义 |
$upload | 上传按钮类型和文字提示 |
$transition | 动画相关 |
$transition-duration | 动画移动速度 |
$transition-function | 淡入淡出、贝塞尔曲线式出入 |
$header | 顶部边距、高度 |
$main | .main内边距 |
$footer | 脚步边距与高度 |
$sm | sm默认大小768px |
$md | md默认大小992px |
$lg | lg默认大小1200px |
$xl | xl默认大小1920px |
$breakpoints | 以上四种大小选择器 |
$breakpoints-spec | 分层级不同大小选择器 |
其他文件变量定义:
/theme-chalk/mixins/config.scss
定义开头-el变量namespace
/theme-chalk/gulpfile.ts
引入gulp,定义三个task任务
首先找到src下面的所有.scss文件
/theme-chalk/src/inde.scss为整个入口文件
/theme-chalk/base.scss
/theme-chalk/common/thransition.scss 动画相关的组件实现淡入淡出等效果
/theme-chalk/button.scss
- /theme-chalk/mixins/config.scss
namespace:b
element-separator:元素分割符 modeifier-sparator:
state-prefix: 状态相关,表示状态的前缀
- /theme-chalk/mixins/function.scss
由于Maps不能转换为纯CSS。作为变量的值或参数传递给CSS函数将会导致错误。使用inspect($value)函数以产生输出字符串。
取出前两位之后后两位之前,中间区域
之后检测是否与对应符号相关
- /theme-chlk/mixins/utils.scss
1行utils-clearfix表示清楚浮动
16行utils-vertical-center表示垂直居中
- /theme-chalk/mixins/mixin.scss
- #{}用于取出变量的方式,$B的值定义了一个class
第56行传入block加上前缀--el,el+名称.#{$B}作为class放入内容,
$block
表示传入的变量,使用@content将我们使用混入时写在大括号中间的内容放在这个class中。最后大括号中的内容都会放入名为el-row的class中例如:
$namespace: 'o' !default;
@mixin b(button) {
$B: o + '-' + button !global;
//$B o-button
.#{$B} {
@content;
}
}
第112行测定select
第151行meb倒置bem
第168行when当...时候,表示状态相关
第176行extend-rule表示扩展规则
第180行share-rule表示共享规则
/theme-chalk/button.scss
b表示一个block定义button相关的样式
46~52表示悬浮与选中状态进行对应颜色的处理
54~59激活(活跃)时处理状态
95when对不同的状态进行处理
例如:el-button--primary
el
在/theme-chalk/mixins/config.scss
下namespace生成button
在/theme-chalk/button.scss第19行@include b(button)
传入的buttonparimary
270~274行,primary,success,warning,danger,info
等定义为type类型
样式覆盖
主题颜色变量如何被覆盖?
在element-plus源码中主题颜色变量的声明后边加上了
!default
后缀,而theme-chalk
通过scss预编译在scss中,!default标签表示:若变量已经赋值则不使用默认值(如果该变量已有值,则其旧值将被覆盖);如果没有赋值就使用默认值。在Scss提供的标志中,仅当变量未定义或其值为 null 时,才会将值分配给该变量。否则,将使用现有值。!default

使用css方法在根目录下重新定义样式

打包之后的文件夹中变量被覆盖,通过修改主题色变量实现


主题换肤
在vue3中进行主题换肤,最简单的是使用scss的@forward或者@use功能引入element-plus的全局基础scss变量,进行覆盖,然后再引入element-plus下的主题scss文件;
@forward和@use的功能基本完全一样
- @forward引入后可以使用 !default覆盖之前文件里使用 !default的变量
- @use引入后不能使用 !default覆盖之前的 !default变量。
更换样式时会覆盖node_modules目录文件内对应样式

scss改变主题
方式一:自定义主题使用scss进行更换
缺点:多套主题一起打包,不能动态加载。切换主题瞬间完成,无延迟等待。使用自定义主题变量覆盖,一次只能使用一种主题
遍历themes变量将子map暴露为全局并针对不同主题定义相关样式。每一次for循环传入相关key的值,for循环取决与主题的套数。一次调用产生多套主题样式,color变量作为key值传入app/vue中。
方式二:使用ElementPlus官网提供插件Vueuse
安装插件:
npm i @vueuse/core
在组件中引入
<template>
<div class="demo">
<el-button type="primary">Primary</el-button>
<el-switch
inline-prompt
v-model="theme"
@click="toggle()"></el-switch>
</div>
</template>
<script lang="ts">
import { defineComponent,ref } from 'vue';
import {useToggle} from '@vueuse/shared'
import { useDark} from '@vueuse/core'
export default defineComponent({
name: 'App',
setup(){
const theme = ref<boolean>(false)
const isDark = useDark({
storageKey:'useDarKEY',
valueDark:'dark',
valueLight:'light',
})
const toggle = useToggle(isDark);
return{
theme,
toggle,
}
}
});
</script>
css动态修改主题
优点:定义多种主题变量,通过js动态修改。根据当前选中的不同主题去读取相关的变量,去覆盖原有的css变量
通过js进行修改。最直观,且是动态的、颜色不限制;
问题:部分组件:el-button、el-tag等主题色不会发生变化,因为组件的主题色,不受css全局变量控制,而是组件中的css变量–el-button-background-color控制;
解决:除了修改css全局变量,同时也要修改组件变量;
- 不足:
- 未修改的组件,有覆盖不全的情况,需要一一排查,比较耗时;
- 不够智能化,element-plus中组件变量未继承自全局变量;
在主题目录下创建深浅主题变量

通过js进行样式主题的覆盖:



使用css覆盖变量时在root下定义相关样式动态改变值
:root { --el-color-primary: #F74023; /* 主题色 */}
Echarts主题变化
监听主题的变化
import { mapState } from 'vuex'export default { computed: { ...mapState(['theme']) }}/*监听theme变化*/export default { watch: { theme () { this.chartInstance.dispose() this.initChart() this.screenAdapter() this.updateChart() } }}
创建/theme/echarts.js进行样式切换数据并导出一个函数,通过主题名称得到对应主题的配置项
const theme = {chalk: { backgroundColor: '#161522', titleColor: '#fff', },}export function getThemeValue (arg) { return theme[arg]}
使用webpack动态导入element-plus
configyreWebpack:{
plugins:[
]
}