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使indexcr在$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;
    }
}
notion image
一维数组:
@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目录
      • css-vars.css定义.dark变量类名以及主题色基础配置。即设置本套主题色为dark前缀,当html标签中设置class=“dark”此主题生效
        notion image
    • src目录
      • |-- 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变量进行重写
        notion image
        /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)传入的button
parimary270~274行,primary,success,warning,danger,info等定义为type类型

样式覆盖

主题颜色变量如何被覆盖?
在element-plus源码中主题颜色变量的声明后边加上了!default后缀,而theme-chalk通过scss预编译
在scss中,!default标签表示:若变量已经赋值则不使用默认值(如果该变量已有值,则其旧值将被覆盖);如果没有赋值就使用默认值。在Scss提供的标志中,仅当变量未定义或其值为 null 时,才会将值分配给该变量。否则,将使用现有值。!default
notion image
使用css方法在根目录下重新定义样式
notion image
打包之后的文件夹中变量被覆盖,通过修改主题色变量实现
notion image
notion image

主题换肤

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

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中组件变量未继承自全局变量;
在主题目录下创建深浅主题变量
notion image
通过js进行样式主题的覆盖:
notion image
notion image
notion image
使用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:[

    ]
}

© shallrise 2025