前言

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

Primary1Primary2复制代码

这样就可以达到只对具体某一个组件的样式进行深度定制了。

那么传统修改组件的默认样式,我们可能会像以下方式:

.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 版本),实现这个功能的话,得有多繁琐,而且如果更换不同主题色的时候