范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文
国学影视

专栏6。CSS架构模式之BEPlus在组件库中的实践

  前言
  Element Plus 的 CSS 架构并没有脱离传统的 CSS 设计模式,主要还是 OOCSS,也就是还是面向对象的思想,即:封装、继承、多态,并且 CSS 变量的加入使用,使得多态这一特性的实现更加丝滑。
  整个 Element Plus 的 CSS 架构核心思想就是,首先将那些公共的 UI 样式进行提取封装成公共 CSS 变量,相当于基础类,然后每个组件又基于公共的 CSS 变量进行继承封装属于每个组件独立的 CSS 变量,相当于子类。这样一旦修改基础类的 CSS 变量,所有继承基础类的组件的样式都会发生改变,这个就是使用 CSS 变量进行封装继承的好处,但如果只想对其中某一个组件的样式做深度的定制,则可以只修改该组件的 CSS 变量,这样就实现了多态。
  值得注意的是CSS 变量可以在运行时进行更改值,这也使得整个组件库的样式更改变化有了非常大的灵活性以及方便性,这意味着你可以动态地改变组件内的个别变量,就可以更好地自定义组件样式,而不需要修改 SCSS 文件重新编译一次。
  此外 Element Plus 默认提供一套主题,CSS 命名采用 BEM 的风格,主要是方便使用者覆盖样式,进行深度定制组件样式。而关于 BEM 我们在本专栏《6. CSS 架构模式之 BEM 在组件库中的实践》的一文中已经进行详细介绍了,本文将不再进行赘述,大家可以自行查看了解。本章节将在前面的文章基础上继续深入研究学习 Element Plus 中的 CSS 架构。
  通过 CSS 变量,Element Plus 默认提供了"白天模式"和"夜间模式"又或着叫:暗黑模式两种皮肤。关于暗黑模式,我们已经在上一篇《10. CSS 系统颜色和暗黑模式的关系及意义》文章中已经进行详细探讨过了,这里也不进行赘述了。
  我们理解了 Element Plus 的 CSS 架构的这些核心思想之后,我们再去研究和阅读 Element Plus 的 CSS 源码则会有一种豁然开朗的感觉,因为它所做的一切都是基于上述的核心思想,所谓理论指导实践,当然也可以在实践中总结理论。当然除了上述思想外,Element Plus 的 CSS 架构还要实现一些组件特有的业务需求,比如按需加载组件的样式如何处理,这些可以在我们详细了解 Element Plus 的 CSS 架构之后再进行具体学习研究。什么是 UI 样式和结构样式
  我们在前言中说到 Element Plus 会把那些公共的 UI 样式进行提取封装成公共 CSS 变量,那么这里所说的 UI 样式是什么呢?
  UI 样式和结构样式(或叫:布局样式),这些概念和思想在传统 CSS 设计模式中就已经存在。布局样式,顾名思义就是让元素如何排列的样式,比如左浮动、右浮动、垂直居中、水平居中、Flex 弹性布局、Grid 网格布局。UI 样式,顾名思义就是外观样式,比如颜色、字体、字体大小、圆角(border-radius)、阴影等主要是颜色。CSS 变量的封装、继承、多态的优势CSS 变量的封装
  我们可以通过在:root 伪类上设置自定义属性也就是 CSS 变量,然后可以在整个页面中需要的地方进行使用,这个可以看作是根据 CSS 变量的特性对 CSS 变量全局作用域的设置,同时这个操作也可以看做是对 UI 样式基础类(父类) 的 封装。
  CSS 变量的继承
  然后在具体的组件中进行继承。比如普通按钮组件的 UI 样式的 CSS 变量如下:
  在el-button 中设置的 CSS 变量只能在设置了.el-button 的 HTML 元素上使用,这个可以看作是根据 CSS 变量的特性对 CSS 变量的局部作用域的设置。CSS 中的多态
  在传统编程中,多态是指不同的子类在继承父类后分别都重写覆盖了父类的方法,即父类同一个方法,在继承的子类中表现出不同的形式。
  同一组件不同的状态有不同的样式实现,这就是 CSS 中的多态。比如 primary 类型的按钮组件的 UI 样式的 CSS 变量设置如下:
  这样在 CSS 书写的时候合理设置前后顺序,再根据 CSS 的生效规则,就可以做到.el-button--primary 中的 CSS 变量将覆盖.el-button 中的 CSS 变量,从而实现多态。
  其实上述的 CSS 使用面向对象的思想:封装继承多态,在传统 CSS 设计模式中也不同的实现,我们目前使用 CSS 变量好像还没体现出其的优势。使用 CSS 变量的优势
  比如 --el-color-primary 属性是在:root 下定义的,然后在 el-button--primary 中通过继承又重新定义出了属于按钮组件新的属性:--el-button-bg-color: var(--el-color-primary),--el-button-border-color: var(--el-color-primary),这样一旦修改--el-color-primary 属性的值,那么--el-button-bg-color 和-el-button-border-color 都将受到影响。因为 --el-color-primary 是定义在:root 下的,是全局作用域的 CSS 变量。如果我们只想修改其中一个组件的 UI 样式,那么我们可以只针对该组件的中定义的 CSS 变量的值进行修改。
  一组默认的 primary 按钮组件及复选框的样式如下:
  根据上面的分析我们去更改全局的 --el-color-primary 属性。:root {   --el-color-primary: green; } 复制代码
  更改之后的样式:
  我们可以看到两个 primary 按钮组件背景式都变成了绿色了,而且所有其他组件如果也使用了 --el-color-primary 变量,颜色都将会变成绿色,例如上图中的复选框组件。如果我们只想 Button 组件中的 primary 类型的组件的话,我们根据上文可以知道在 el-button--primary 中通过继承又重新定义出了属于按钮组件新的属性:--el-button-bg-color: var(--el-color-primary)。这样我就可以只修改 --el-button-bg-color 即可。由于作用域的问题,在 class 中定义的 CSS 变量优先级要比在 :root 中的要高,所以修改 --el-button-bg-color 的值,我们需要在 class 中进行修改。.button-bg-color {   --el-button-bg-color: green; } 复制代码
  然后在模板中要进行修改的 Button 组件进行添加相应的 class    Primary1     Primary2 复制代码
  这样就可以达到只对具体某一个组件的样式进行深度定制了。
  那么传统修改组件的默认样式,我们可能会像以下方式:.transfer-content{     .el-form-item__content{         .el-input{             .el-input__inner{                 border-color: #DCDFE6;             }         }         .el-form-item__error{             width: 200px;         }     } } 复制代码
  为了少写 class 需要像套娃一样。更方便地共享主题色
  比如我现在的项目中引用了 Element Plus,那么我也可以在我的项目中通过 CSS 变量使用 Element Plus 中的样式设置。比如说我们知道 primary 类型的组件的主题色是使用的 CSS 变量是 --el-color-primary,那么我们也可以在我们的项目中使用这个变量。Primary 稀土掘金,一个帮助开发者成长的社区 复制代码
  在本地项目中使用 Element Plus 组件库中的变量 --el-color-primary, CSS 样式如下:.juejin{   color: var(--el-color-primary) } 复制代码
  渲染结果:
  我们发现居然可以轻松使用 Element Plus 组件库中的变量,这样一来我们构建本地项目的 CSS 架构可以和 Element Plus 更加丝滑结合。如果小伙伴使用过 Vue2 的组件库 Element ui (也就是 Element Plus 的 Vue2 版本),实现这个功能的话,得有多繁琐,而且如果更换不同主题色的时候,目前 Element Plus 中使用 CSS 变量,我们只需要更改 CSS 变量的值,即可。更好地动态更改主题色
  以下是花裤衩大佬在他的开源项目中实现的 Vue2 版本的 element ui (也就是 Element Plus 的 Vue2 版本)的动态更改主题色的功能。
  它的实现原理,花裤衩大佬在其文档中也有进行说明:"element-ui 2.0 版本之后所有的样式都是基于 SCSS 编写的,所有的颜色都是基于几个基础颜色变量来设置的,所以就不难实现动态换肤了,只要找到那几个颜色变量修改它就可以了。 首先我们需要拿到通过 package.json 拿到 element-ui 的版本号,根据该版本号去请求相应的样式。拿到样式之后将样色,通过正则匹配和替换,将颜色变量替换成你需要的,之后动态添加 style 标签来覆盖原有的 css 样式。"
  代码地址:@/components/ThemePicker。
  那么在 Element Plus 使用了 CSS 变量之后,进行动态换肤则变得简单很多了。我们只需要修改相应的 CSS 变量值即可。
  我们可以通过 useCssVar | VueUse 来控制 CSS 变量的更改和读取。Element Plus 中的主要颜色的 CSS 变量是 --el-color-primary,所以我们只需要控制和更改这个 CSS 变量的值即可。import { ref } from "vue"; import { useCssVar } from "@vueuse/core" const color = useCssVar("--el-color-primary", null) const checked = ref(true) const value = ref(true) 复制代码
  测试模板:请选择主题色    点击关注 复制代码
  渲染结果:
  我们可以看到 Element Plus 使用 CSS 变量之后,使用社区的 Vue3 Hooks 函数 useCssVar | VueUse 动态更改主题色是非常简单的了。useCssVar 的原理
  useCssVar 的原理其实也很简单,就是对 CSS 变量读写的 DOM 操作方法的封装。
  获取及设置 css 变量的 DOM 方法。
  获取全局 CSS 变量const el = window.document.documentElement  // 获取 css 变量 const value = getComputedStyle(el).getPropertyValue(`--el-color-primary`) 复制代码
  获取元素中的 CSS 变量const el = document.getElementById("xxx")  // 获取 css 变量 const value = getComputedStyle(el).getPropertyValue(`--el-color-primary`) 复制代码
  设置 CSS 变量// 设置 css 变量 el.style.setProperty("--el-color-primary", "red") 复制代码
  那么在 useCssVar 中则不再通过 DOM 方法(document.getElementById)获取元素,而且是事先通过 Vue 中的模板引用 ref 来获取元素。
  useCssVar 函数如下:import { ref, computed, watch } from "vue"  const defaultWindow = window function useCssVar(   prop,   target,   { window = defaultWindow, initialValue = "" } = {}, ) {   const variable = ref(initialValue)   const elRef = computed(() => target || window?.document?.documentElement)   // 获取 CSS 变量值   watch(     [elRef, () => prop],     ([el, prop]) => {       if (el && window) {         const value = window.getComputedStyle(el).getPropertyValue(prop)?.trim()         variable.value = value || initialValue       }     },     { immediate: true },   )   // 设置 CSS 变量值   watch(     variable,     (val) => {       if (elRef.value?.style)         elRef.value.style.setProperty(prop, val)     },   )    return variable } 复制代码
  那么到这里动态修改组件主题色,即便加上 useCssVar 函数的实现逻辑,也比原来 elmenent ui 中没有使用 CSS 变量的实现要简单得多。
  我们现在了解 Element Plus CSS 的一些实现思想和具体的项目应用例子之后,我们接下来就进行深入了解具体的实现逻辑。SCSS 和 CSS变量的架构实践
  我们根据上文已经知道 Element Plus 的 CSS 架构核心思想就是,首先将那些公共的 UI 样式进行提取封装成公共 CSS 变量,相当于基础类,然后每个组件又基于公共的 CSS 变量进行继承封装属于每个组件独立的 CSS 变量,相当于子类。如何封装公共 CSS 变量
  所以首先我们需要封装公共的 CSS 变量,我们知道简单的写法就如下::root {   --el-color-primary: green; } 复制代码
  但这样一来便直接写死了,后续不好拓展,比如 CSS 变量的前缀 el,我们知道是可以动态修改的,如果已经写死了的话,则后期拓展性则直接堵死了。所以我们借助 SCSS 的编程能力,通过一个 SCSS 函数来生成 CSS 变量,然后在 SCSS 函数里面则可以做相关的业务逻辑的动态设置了。
  在 Element Plus 中是通过设置一个 set-css-var-value 的 SCSS 函数来设置 CSS 变量的。设置 CSS 变量的文件是在 packages/theme-chalk/src/var.scss。:root {   @include set-css-var-value("color-white", "#fff"); } 复制代码
  像这类设置 CSS 变量的 SCSS 函数在 packages/theme-chalk/src/mixins/_var.scss 文件中进行设置。@mixin set-css-var-value($name, $value) {   #{joinVarName($name)}: #{$value}; } 复制代码
  set-css-var-value 函数则是通过 joinVarName 函数根据参数 $name 拼接 CSS 变量名。joinVarName 函数则是在 packages/theme-chalk/src/mixins/function.scss 文件中定义的。@function joinVarName($list) {   $name: "--" + config.$namespace;   @each $item in $list {     @if $item != "" {       $name: $name + "-" + $item;     }   }   @return $name; } 复制代码
  参数 $list 是一个数组,例如 joinVarName(("button", "text-color")) 则可以生成 --el-button-text-color。这里我们可以看到前缀 el 是通过 SCSS 配置变量 config.$namespace,那么在开发项目中则可以修改 config.$namespace 变量更改 CSS 变量的前缀。
  在项目的开发阶段我们可以通过以下方式更改 Element Plus 的 CSS 变量前缀。@forward "element-plus/theme-chalk/src/mixins/config.scss" with (   $namespace: "ep" ); 复制代码样式目录文件架构
  到此我们有必要对 Element Plus 中的样式目录文件的架构组成做一个配置解析,以便我们从一个更目录架构的维度去了解 Element Plus 中的样式架构思想。  packages       theme-chalk           src               common                   var.scss // SCSS 变量配置文件               mixins                   _var.scss // CSS 变量相关的 SCSS 自定义函数                   config.scss // 配置文件                   function.scss // 全局的 SCSS 自定义函数                   mixins.scss  // BEM 相关函数               base.scss // 基础必须要引用的文件               index.scss // 全量导入组件库的 CSS 入口文件               var.scss   // CSS 变量配置文件               reset.scss // 默认样式重置配置文件               button.scss // 按需导入组件的 CSS 文件           package.json // 主题包的 npm 配置文件 复制代码
  从文件目录架构和结合上文进行理解,我们需要做的就是通过 theme-chalk/src/common/var.scss 中的 SCSS 变量配置在 theme-chalk/src/var.scss 中进行封装生成全局的 CSS 变量,在这个过程中使用到的有关生成 CSS 变量的 SCSS 自定义函数则在 theme-chalk/src/mixins/_var.scss 文件中进行设置。
  因为要实现按需导入组件,所以各个组件单独一个 CSS 文件。例如 Button 组件的 CSS 文件对应就是 button.scss 文件。在编写组件 CSS 样式中需要使用到的 BEM 的 SCSS 自定义函数则在 theme-chalk/src/mixins/mixins.scss 中,其他的 SCSS 自定义函数则在 theme-chalk/src/mixins/function.scss 中。
  base.scss 文件则是引用那些必须的文件,比如 var.scss 文件是必须,这类文件则放在 base.scss 文件中。在全量导入组件库的 CSS 入口文件 index.scss 中最先引入的便是 base.scss 文件。将来组件的自己的按需导入的 CSS 文件,例如 packages/components/button/style/index.ts 也就是 Button 组件的按需导入的 CSS 文件,其中也是需要最先导入 base.scss 文件,再进行导入 button.scss 文件。SCSS 样式变量
  我们希望在设置生成 CSS 变量的时候是根据 SCSS 的变量动态生成的,这样我们用户在使用的时候,如果需要进行大规模替换样式,可以通过直接覆盖 Element Plus 中的 SCSS 变量来实现。:root {   @include set-css-var-value("color-white", $color-white); } 复制代码
  上述 $color-white 变量设置在 packages/theme-chalk/src/common/var.scss 文件中。
  我们知道 Element Plus 中有 primary、success、warning、danger、error、info 六种类型颜色。我们需要对这六种类型的颜色设置 SCSS 变量。$colors: () !default; $colors: map.deep-merge(   (     "white": #ffffff,     "black": #000000,     "primary": (       "base": #409eff,     ),     "success": (       "base": #67c23a,     ),     "warning": (       "base": #e6a23c,     ),     "danger": (       "base": #f56c6c,     ),     "error": (       "base": #f56c6c,     ),     "info": (       "base": #909399,     ),   ),   $colors );  $color-white: map.get($colors, "white") !default; $color-black: map.get($colors, "black") !default; $color-primary: map.get($colors, "primary", "base") !default; $color-success: map.get($colors, "success", "base") !default; $color-warning: map.get($colors, "warning", "base") !default; $color-danger: map.get($colors, "danger", "base") !default; $color-error: map.get($colors, "error", "base") !default; $color-info: map.get($colors, "info", "base") !default; 复制代码
  先设置一个总的 $colors 变量,然后再单独设置每一个具体类型的变量,变量值是继承 $colors 变量,这样将来只要修改 $colors 中的变量值,后续每个继承 $colors 变量的 SCSS 变量都将发生改变。
  值得注意的是 $colors: () !default; 中 () 写法是 SCSS 中变量空数组的表示方式,!default; 表示默认变量,将来可以使用 @forward 修改默认变量。那么为什么不直接把相关变量设置写到默认变量中呢?因为将来用户在使用 @forward 修改 SCSS 中的默认变量时,有可能只修改其中部分的选项,并不是全部修改,如果相关变量写在默认变量中,那么使用 @forward 修改默认变量时,就不能修改部分选项了。目前这种通过 map.deep-merge 合并默认变量的选项的方式则可以达到将来只修改部分选项的目的,变得更加灵活。
  修改 Element Plus 中的 SCSS 变量:// styles/element/index.scss /* 只需要重写你需要的即可 */ @forward "element-plus/theme-chalk/src/common/var.scss" with (   $colors: (     "primary": (       "base": green,     ),   ), );  // 如果只是按需导入,则可以忽略以下内容。 // 如果你想导入所有样式: // @use "element-plus/theme-chalk/src/index.scss" as *; 复制代码
  这样就只会修改 primary 选项的值。如何设置组件的多种类型主题
  Element Plus 中 Button 组件有 primary、success、warning、danger、error、info 六种类型颜色,而且在不同状态下的都是这六种颜色的相近的颜色。
  比如 primary 类型的按钮的默认的背景色是 --el-color-primary,鼠标悬浮的时候则是 --el-color-primary-light-3。
  plain 样式的 primary 类型的按钮,即:Primary 的背景色则是 --el-color-primary-light-9。
  --el-color-primary 和 --el-color-primary-light-3 都是相近的颜色,此外还有 --el-color-primary-light-7, --el-color-primary-light-8,--el-color-primary-light-9。详细如下图:
  这是根据使用场景的定义的颜色粒度。这么多类型这么多不同的颜色,在 Element Plus 中是怎么实现的呢?
  首先定义一个 SCSS 函数 set-color-mix-level 拓展 $colors 变量中的颜色。@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.p($number, 10))           ),       ),     ),     $colors   ) !global; // 将局部变量转换为全局变量 } 复制代码
  Sass:RGB颜色函数-Mix() 函数
  Mix 函数是将两种颜色根据一定的比例混合在一起,生成另一种颜色。其使用语法如下: mix($color-1,$color-2,$weight); $color-1 和 $color-2 指的是你需要合并的颜色,颜色可以是任何表达式,也可以是颜色变量。$weight为合并的比例(选择权重),默认值为 50%,其取值范围是 0~1 之间。它是每个 RGB 的百分比来衡量,当然透明度也会有一定的权重。默认的比例是 50%,这意味着两个颜色各占一半,如果指定的比例是 25%,这意味着第一个颜色所占比例为 25%,第二个颜色所占比例为75%。
  math.percentage 将一个不带单位的数转换成百分比值。
  与其他数学运算不同,Sass 中的除法是通过 math.p() 函数完成的。
  !global 将局部变量转换为全局变量
  生成 light 模式的颜色,就是亮色模式。// types $types: primary, success, warning, danger, error, info;  @each $type in $types {   @for $i from 1 through 9 {     @include set-color-mix-level($type, $i, "light", $color-white);   } } 复制代码
  执行上述代码就等于往各类型中添加新的变量颜色,相当于下面的代码的效果。$colors:   (     "primary": (       "base": #409eff,       "light-1": 通过mix生成的新颜色,       "light-2": 通过mix生成的新颜色,       "light-3": 通过mix生成的新颜色,       // ...     ),     "success": (       "base": #67c23a,       "light-1": 通过mix生成的新颜色,       "light-2": 通过mix生成的新颜色,       "light-3": 通过mix生成的新颜色,       // ...     ),     "warning": (       "base": #e6a23c,       "light-1": 通过mix生成的新颜色,       "light-2": 通过mix生成的新颜色,       "light-3": 通过mix生成的新颜色,       // ...     ),     // ...   ) 复制代码
  对应的还有浅色模式的颜色生成。// types $types: primary, success, warning, danger, error, info;  @each $type in $types {   @include set-color-mix-level($type, 2, "dark", $color-black); } 复制代码
  我们可以通过 SCSS 提供的 @debug 函数进行查看生成后的 $colors,即:@debug $colors。然后可以在终端看到以下输出:( info: ( "dark-2": #73767a,  "light-9": #f4f4f5,  "light-8": #e9e9eb,  "light-7": #dedfe0,  "light-6": #d3d4d6,  "light-5": #c8c9cc,  "light-4": #bcbec2,  "light-3": #b1b3b8,  "light-2": #a6a9ad,  "light-1": #9b9ea3,  "base": #909399),  error: ( "dark-2":#c45656,  "light-9": #fef0f0,  "light-8": #fde2e2,  "light-7": #fcd3d3,  "light-6": #fbc4c4,  "light-5": #fab6b6,  "light-4": #f9a7a7,  "light-3": #f89898,  "light-2": #f78989,  "light-1": #f67b7b,  "base": #f56c6c),  danger: ( "dark-2": #c45656,  "light-9": #fef0f0,  "light-8": #fde2e2,  "light-7": #fcd3d3,  "light-6": #fbc4c4,  "light-5": #fab6b6,  "light-4": #f9a7a7,  "light-3": #f89898,  "light-2": #f78989,  "light-1": #f67b7b,  "base": #f56c6c),  warning: ( "dark-2": #b88230,  "light-9": #fdf6ec,  "light-8": #faecd8, "light-7": #f8e3c5,  "light-6": #f5dab1,  "light-5": #f3d19e,  "light-4": #f0c78a,  "light-3": #eebe77,  "light-2": #ebb563,  "light-1": #e9ab50, "base": #e6a23c),  success: ( "dark-2": #529b2e,  "light-9": #f0f9eb,  "light-8": #e1f3d8,  "light-7": #d1edc4,  "light-6": #c2e7b0,  "light-5": #b3e19d,  "light-4": #a4da89,  "light-3": #95d475,  "light-2": #85ce61,  "light-1": #76c84e,  "base": #67c23a),  primary: ( "dark-2": #337ecc,  "light-9": #ecf5ff,  "light-8": #d9ecff,  "light-7": #c6e2ff,  "light-6": #b3d8ff,  "light-5": #a0cfff,  "light-4": #8cc5ff,  "light-3": #79bbff,  "light-2": #66b1ff,  "light-1": #53a8ff,  "base": #409eff),  "white": #ffffff,  "black": #000000 ) 复制代码
  接下来我们就要去生成以下的全局 CSS 变量了。
  这些属于亮色模式的样式(更多可以查看《10. CSS 系统颜色和暗黑模式的关系及意义》),所以我们要另起一个 :root 并且加上 color-scheme: light,告诉浏览器使用亮色模式进行渲染。我们通过循环 primary, success, warning, danger, error, info 再通过自定义 SCSS 函数 set-css-color-type 去生成相关的 CSS 变量。:root {   color-scheme: light;    // --el-color-#{$type}   // --el-color-#{$type}-light-{$i}   @each $type in (primary, success, warning, danger, error, info) {     @include set-css-color-type($colors, $type);   } } 复制代码
  set-css-color-type 函数。@mixin set-css-color-type($colors, $type) {    // 生成 --el-color-#{$type}   @include set-css-var-value(("color", $type), map.get($colors, $type, "base"));    // 生成 --el-color-#{$type}-light-{$i}   @each $i in (3, 5, 7, 8, 9) {     @include set-css-var-value(       ("color", $type, "light", $i),       map.get($colors, $type, "light-#{$i}")     );   }   // 生成 --el-color-#{$type}-dark-2   @include set-css-var-value(     ("color", $type, "dark-2"),     map.get($colors, $type, "dark-2")   ); } 复制代码
  上述主要是讲解了生成 primary, success, warning, danger, error, info 相关的全局 CSS 变量。以分组生成 CSS 变量
  在 Element Plus 的 SCSS 架构中还有一个很重要的思想,就是以分组的模式生成不通过分组的 CSS 变量。比如文本颜色,会有很多种颜色,那么文本颜色就为一个分组,还有背景颜色,边框颜色,还有我们每个组件的需要用到的 CSS 变量,也是以一个组件为分组生成一组 CSS 变量。
  背景颜色$bg-color: () !default; $bg-color: map.merge(   (     "": #ffffff,     "page": #f2f3f5,     "overlay": #ffffff,   ),   $bg-color ); 复制代码
  文本颜色$text-color: () !default; $text-color: map.merge(   (     "primary": #303133,     "regular": #606266,     "secondary": #909399,     "placeholder": #a8abb2,     "disabled": #c0c4cc,   ),   $text-color ); 复制代码
  边框颜色$border-color: () !default; $border-color: map.merge(   (     "": #dcdfe6,     "light": #e4e7ed,     "lighter": #ebeef5,     "extra-light": #f2f6fc,     "dark": #d4d7de,     "darker": #cdd0d6,   ),   $border-color ); 复制代码
  这些分组都是通过定义一个 SCSS 函数 set-component-css-var 来生成。:root {   color-scheme: light;   @include set-component-css-var("bg-color", $bg-color);   // --el-text-color-#{$type}   @include set-component-css-var("text-color", $text-color);   // --el-border-color-#{$type}   @include set-component-css-var("border-color", $border-color); } 复制代码
  SCSS 函数 set-component-css-var。@mixin set-component-css-var($name, $variables) {   @each $attribute, $value in $variables {     @if $attribute == "default" {       #{getCssVarName($name)}: #{$value};     } @else {       #{getCssVarName($name, $attribute)}: #{$value};     }   } } 复制代码
  到目前为止,我们上文讲解了那么多,都是为了我们开篇所讲的核心思想,将那些公共的 UI 样式进行提取封装成公共 CSS 变量。Button 组件的样式实现
  我们上文说到每个组件会基于公共的 CSS 变量进行继承封装属于每个组件独立的 CSS 变量,那么我们以 Button 组件为例进行说明,因为 Button 组件的业务代码和 HTML 结构我们已经在前面的章节《9. 组件开发中 Vue3 相关知识的应用与解析及 Button 组件的实现》实现了,现在实现样式部分。
  首先我们要实现组件自身基于公共 CSS 变量进行封装的 CSS 变量。通过上面我们知道为了方便拓展和维护,我们将 CSS 变量值的设置在 SCSS 的变量文件中。
  先在 packages	heme-chalksrccommonvar.scss 中设置 Button 相关变量。$button: () !default; $button: map.merge(   (     "font-weight": getCssVar("font-weight-primary"),     "border-color": getCssVar("border-color"),     "bg-color": getCssVar("fill-color", "blank"),     "text-color": getCssVar("text-color", "regular"),     // ...   ),   $button ); 复制代码
  这里我们看到 Button 组件相关 UI 样式的 SCSS 变量的值是 getCssVar 函数获取的全局的 CSS 变量,这样将来生成新的 CSS 变量的值就是继承于全局的 CSS 变量。
  然后在 packages/theme-chalk/src/button.scss 文件中设置 Button 组件相关的 CSS 变量。@use "sass:map"; @use "common/var" as *; @use "mixins/mixins" as *; @use "mixins/var" as *;  @include b(button) {   @include set-component-css-var("button", $button); } 复制代码
  @mixin b() 方法是我们前面文章中说到生成 BEM 的 CSS 命名规范函数,详细了解可以看这篇文章 《6. CSS 架构模式之 BEM 在组件库中的实践》。set-component-css-var 方法是我们上文中说过了的,它会根据设置好的一组 SCSS 变量进行生成对应的 CSS 变量。
  经过上面的 SCSS 代码就会生成默认的 Button 组件的 CSS 变量。
  正如上文所说 Button 组件生成的 CSS 变量的值是全局的 CSS 变量,当然有些特别的变量不是。
  同样其他组件的 CSS 变量也是如此生成。比如 checkbox 组件。@include b(checkbox) {   @include set-component-css-var("checkbox", $checkbox); } 复制代码
  生成组件 CSS 变量之后,我们就可以去设置组件的样式了。@include b(button) {   display: inline-flex;   justify-content: center;   align-items: center;    line-height: 1;   height: map.get($input-height, "default");   white-space: nowrap;   cursor: pointer;   color: getCssVar("button", "text-color");   text-align: center;   box-sizing: border-box;   outline: none;   transition: 0.1s;   font-weight: getCssVar("button", "font-weight");   user-select: none;   vertical-align: middle;   -webkit-appearance: none;   background-color: getCssVar("button", "bg-color");   border: getCssVar("border");   border-color: getCssVar("button", "border-color"); } 复制代码
  生成的最终的页面 CSS 结果如下:
  通过结果我们可以清晰看出组件的相关 UI 样式是获取组件本身的 CSS 变量。主要是通过 getCssVar 这个函数来实现的。那么我们来看看 getCssVar 函数的实现。@function getCssVar($args...) {   @return var(#{joinVarName($args)}); }  @function joinVarName($list) {   $name: "--" + config.$namespace;   @each $item in $list {     @if $item != "" {       $name: $name + "-" + $item;     }   }   @return $name; } 复制代码
  其实很简单就是通过传进来的参数进行拼接成 CSS 变量的名称,在 Button 组件中设置 getCssVar("button", "bg-color") 将会生成 --el-button-bg-color,那么获取的就是组件的 CSS 变量,如果是 getCssVar("bg-color") 将会生成 --el-bg-color,那么获取的就是全局的 CSS 变量。在 packages	heme-chalksrccommonvar.scss 中设置 Button 相关变量时,通过 getCssVar 获取的 CSS 变量也是这个原理。
  Button 组件和其他组件的样式有一个最大的不同就是,Button 组件默认是有 primary, success, warning, danger, info,加上默认的主题色,一共有六种主题色,而我们上面实现的是默认的主题色,也是所有其他组件的默认实现方式。
  实现的原理也很简单就是上文所说的多态,具体就是根据不同的 classname 来进行实现不同的 UI 样式的 CSS 变量,对应的分别是 el-button--primary、el-button--success、el-button--warning、el-button--danger、el-button--info。在 BEM 规范中 primary, success, warning, danger, info 属于 Button 组件的不同修改器,要生成修改器的 classname 则需要通过 @mixin m()。
  具体实现如下:@include b(button) {   @each $type in (primary, success, warning, danger, info) {     @include m($type) {       @include button-variant($type);     }   } } 复制代码
  再定义一个 button-variant 函数进行根据不同类型生成不同的修改器的 CSS 变量。又因为这个函数是 Button 组件特有的,所以新建一个专门属于 Button 组件的 SCSS @mixin 函数文件。也就是 packages/theme-chalk/src/mixins/_button.scss。@mixin button-variant($type) {   $button-color-types: (     "": (       "text-color": (         "color",         "white",       ),       "bg-color": (         "color",         $type,       ),       "border-color": (         "color",         $type,       ),      // ...     ),   );    @each $type, $typeMap in $button-color-types {     @each $typeColor, $list in $typeMap {       @include css-var-from-global(("button", $type, $typeColor), $list);     }   } } 复制代码
  css-var-from-global 函数的实现@mixin css-var-from-global($var, $gVar) {   $varName: joinVarName($var);   $gVarName: joinVarName($gVar);   #{$varName}: var(#{$gVarName}); } 复制代码
  可以看到原理也很简单,就是根据第一个参数生成对应 Button 组件的 CSS 变量名称,根据第二个参数生成对应全局的 CSS 变量名称,然后通过原生 CSS 函数 var 读取全局 CSS 变量。
  经过上述设置,我们在 play 项目中进行测试我们编写的 Button 组件的样式Default Primary Success Info Warning Danger 复制代码
  我们启动 play 项目
  最终渲染结果如下:
  至此我们讲解了 Element PLus 中的 CSS 架构思想,以及通过实现 Button 组件的样式来实践 Element PLus 中的 CSS 架构思想。暗黑模式
  通过 CSS 变量,Element Plus 默认提供了"白天模式"和"夜间模式" (又或着叫:暗黑模式) 两种皮肤。我们在上文中其中主要实现的都是浅色模式的颜色,因为默认就是浅色模式。暗黑模式的实现其实跟 Button 组件中的不同类型的主题的实现是本质原理是一样的,就是根据 CSS 变量的优先级进行覆盖。只要设置暗黑模式中的 CSS 变量比浅色模式中的 CSS 变量优先级更高,就可以达到当页面切换到暗黑模式的时候,暗黑模式中的 CSS 变量就会覆盖浅色模式的 CSS 变量。
  在项目中如何使用,可以查看官方文档 Element Plus 终于支持了黑暗模式。总结
  Element Plus 的 CSS 架构并没有脱离传统的 CSS 设计模式,主要还是 OOCSS,也就是还是面向对象的思想,即:封装、继承、多态,并且 CSS 变量的加入使用,使得多态这一特性的实现更加丝滑。
  整个 CSS 架构核心思想就是,首先将那些公共的样式进行提取封装成公共 CSS 变量,相当于基础类,然后每个组件又基于公共的 CSS 变量进行继承封装属于每个组件独立的 CSS 变量,相当于子类。这样一旦修改基础类的 CSS 变量,所有继承基础类的组件的样式都会发生改变,这个就是使用 CSS 变量进行封装继承的好处,但如果只想对其中某一个组件的样式做深度的定制,则可以只修改该组件的 CSS 变量,这样就实现了多态。
  值得注意的是CSS 变量可以在运行时进行更改值,这也使得整个组件库的样式更改变化有了非常大的灵活性以及方便性,这意味着你可以动态地改变组件内的个别变量,就可以更好地自定义组件样式,而不需要修改 SCSS 文件重新编译一次。
  我们理解了 Element Plus 的 CSS 架构的这些核心思想之后,我们再去研究和阅读 Element Plus 的 CSS 源码则会有一种豁然开朗的感觉,因为它所做的一切都是基于上述的核心思想,所谓理论指导实践,当然也可以在实践中总结理论。当然除了上述思想外,Element Plus 的 CSS 架构还要实现一些组件特有的业务需求,比如按需加载组件的样式如何处理,这些可以在我们详细了解 Element Plus 的 CSS 架构之后再进行具体学习研究。

生活记录心情困扰我们三年的口罩问题,随着新年的结束也算是划上了句号!压抑很久的心情也得以平静,还好公司还在继续虽然已经显得摇摇欲坠,至少该有的还可以继续!我始终相信有人就有一切,兄弟还在,事业林徽因经典语段21山中一个夏夜,深得像没有底一样黑影,松林密密的周围没有点光亮。对山闪着只一盏灯两盏象夜的眼,夜的眼在看!2到如今我的船仍然在海面飘,细弱的桅杆常在风涛里摇。到如今太阳只在我背后徘转眼来这座城快20年了!它此刻的美也定义了我依恋她怀抱的理由头条创作挑战赛爱上一个人和爱上一座城的理由是一样的,哪有那么多理由?爱,都是说不清道不明的。真正的爱,就应该是无缘无故的爱上,根本没有那么多的附加。而如今,爱成了这个世界上最奢侈的退休后,如果你一个朋友都没有,那么恭喜你的福气来了经济学家林采宜曾说退休是人生的一个拐点,此处叶落,别处花开。时光如指间沙,悄然流逝,蓦然回首时,曾经意气风发的岁月,已经一去不复返。回首前半生的过往,我们似乎不曾真正地为自己活过。标压i712700H140W满功耗RTX3060,联想拯救者Y9000P2022款评测一序言同价位游戏笔记本和PC主机你会怎么选?如果把这个问题放在虚拟币狂欢开始之前,我想多数游戏玩家会毫不犹豫地选择后者,追求性能的他们很难接受游戏笔记本电脑的低功耗残血版硬件以及那飞跃奇异点微星笔记本13thRTX40新品媒体抢鲜会CNMO新闻微星笔记本全新13thRTX40笔记本已正式发布,为了可以让媒体KOL朋友能率先第一时间近距离感受更强性能更人性化体验的微星笔记本新品。也举办了一场主题为飞跃奇异点微星白嫖党福利!这7个珍藏多年的资源网站,20T硬盘都装不下!总是到处找资源?今天和大家分享白嫖党福利,这7个珍藏多年的资源网站,资源非常丰富,你想要的资源基本上全都有。Unsplash这里提供了免费且高清的图片素材,每10天更新10张照片,帕丽斯希尔顿产子后首现身,被老公牵着心情放飞,皮相仍是最熟土味据每日邮报2月10日消息,帕丽斯希尔顿刚刚通过代孕生下孩子,几周后,有人看到她在一次外出活动中焕发出初为人母的光彩。周五,这位41岁的女继承人和丈夫卡特雷姆(CarterReum)新年奋进新征程,汕头市举行纺织服装产业发展促进大会近日,汕头市举行汕头市纺织服装产业协会第一届第二次会员大会暨纺织服装产业发展促进大会,推动汕头市纺织服装产业新一年大步向前。汕头市举行纺织服装产业发展促进大会。纺织服装产业作为汕头我们为何没有找到外星人,是我们技术不行?还是根本没有?地球上的科学家研究了所有已知的动物,从最小的蚂蚁到最大的鲸鱼。作为地球上的一个物种,我们自然地会好奇我们周围的一切。但是外星物种也是一样的吗?无论他们在哪里,他们也都会有类似的好奇科大讯飞类ChatGPT技术今年5月落地AI学习机产品先用近段时间ChatGPT带火了AI领域,尤其是极为聪明的对话逻辑和知识体系,简直秒杀了以往的AI助手等产品。根据最新消息,马上国内就能用上类似的产品了。今日,有投资者在投资者互动平台
Python打包神器Nuitka什么是Nuitka?Nuitka是Python的编译器,由Python自举。功能很多,其中之一就是把Python脚本直接编译成可执行文件,此功能与Pyinstaller相似。NuiCSGOAurora战队官宣签下VP青训选手Lattykk1Aurora战队官方宣布了他们已经签下了前VP青训选手Lattykk。VP与Aurora之间的交易细节并没有被披露,Lattykk将继续担任队内狙击手的角色。就在前几天,Auro阳过之后怎么办?怎样做自己健康第一责任人?速来看妙招2022年12月,所有人最关心的莫过于一件事阳了没?万一阳了,怎么办?少数有心人早早屯好药,即便中招也不会太焦虑,而绝大部分人完全没有做好突然放开的准备,一时间人心惶惶,网上线下到任性的电商顾客今天,想吐槽一下电商平台上任性的顾客。其实有这个想法是源于,我今年有机会接触到了电商后台的商家。由于疫情原因,无所事事又闲不住的我,找了一份兼职小时工的活儿给一家在某宝电商平台卖女法国Thales和德国MarvelFusion将升级罗马尼亚ELINP激光系统长三角G60激光联盟导读据悉,法国Thales和德国MarvelFusion将升级罗马尼亚ELINP激光系统。合作伙伴将升级激光系统,研究核聚变作为未来的新能源开发国防航空航天和高芯片之母EDA国产替代加速,合见工软立足验证,进军全流程近日在厦门举办的ICCAD2022上,合见工软产品工程副总裁孙晓阳先生在会上表示,现在国家对半导体EDA的扶持力度在不停地加大,整个EDA行业投资环境也有改善。合见工软在这种利好的五大小花跨年斗艳,杨紫膀大腰圆,关晓彤妆容再翻车,虞书欣完胜昨晚的跨年夜,各个卫视都使出了杀手锏,新晋小花们也都精彩亮相。有的造型惊艳,有的眼前一亮,有的彻底翻车,还有人一言难尽。关晓彤妆容老气十足错误妆容真能让人瞬间奔四,这个化妆师真狠,欢呼侃球布伦特福德VS利物浦布伦特福德在休赛期前一鸣惊人,客场绝杀了蓝月亮,开赛后主场打平了热刺又在客场完胜了铁锤帮,令人刮目相看。视频加载中上述的评点只是欢呼对本场比赛的一些想法!朋友们可以把您对这场比赛的全民皆兵喜提12连胜,11冠王隐隐再现冠军相,两大隐患仍需注意2022生机大会CBA常规赛,广东队对阵同曦。广东进入第二阶段状态大勇,一波11连胜势不可当,甚至还终结了领头羊浙江队的全胜纪录。南京同曦则因为赛程原因,近来接连输给北京辽宁和吉林各大卫视2023跨年晚会,女明星争芳斗艳,谁最性感?各大卫视的2023跨年晚会圆满落幕,各路女明星争芳斗艳,成为亮点之一。女星们不仅拼实力拼排场,在造型上更是铆足了劲儿,一定要在2023年第一次亮相扬眉兔气。小编做了一个盘点,供大家成熟女人的街拍大赏,为什么更耐人寻味?3个时髦套路,够狠文洛薇Hi,我是洛薇,继续我们的时尚穿搭之旅,变美永远不迷路。冬天的时尚街拍,都有哪些看点?今天,来聊它吧。冬天的美,在于静赏。寒风萧杀下,天地一片静谧。生机与希望,也在此刻被深深