element-ui
中的样式文件全部在theme-chalk
文件夹下,其目录结果主要包括common
、fonts
、mixins
和一些组件样式文件,组件样式文件除了日期组件是一个文件夹外,其它的都是一个独立的.scss
文件。其目录结构如下所示:
├── README.md
├── gulpfile.js
├── package.json
└── src
├── alert.scss
├── aside.scss
├── autocomplete.scss
├── avatar.scss
├── backtop.scss
├── badge.scss
├── base.scss
├── breadcrumb-item.scss
├── breadcrumb.scss
├── button-group.scss
├── button.scss
├── calendar.scss
├── card.scss
├── carousel-item.scss
├── carousel.scss
├── cascader-panel.scss
├── cascader.scss
├── checkbox-button.scss
├── checkbox-group.scss
├── checkbox.scss
├── col.scss
├── collapse-item.scss
├── collapse.scss
├── color-picker.scss
├── common
│ ├── popup.scss
│ ├── transition.scss
│ └── var.scss
├── container.scss
├── date-picker
│ ├── date-picker.scss
│ ├── date-range-picker.scss
│ ├── date-table.scss
│ ├── month-table.scss
│ ├── picker-panel.scss
│ ├── picker.scss
│ ├── time-picker.scss
│ ├── time-range-picker.scss
│ ├── time-spinner.scss
│ └── year-table.scss
├── date-picker.scss
├── dialog.scss
├── display.scss
├── divider.scss
├── drawer.scss
├── dropdown-item.scss
├── dropdown-menu.scss
├── dropdown.scss
├── fonts
│ ├── element-icons.ttf
│ └── element-icons.woff
├── footer.scss
├── form-item.scss
├── form.scss
├── header.scss
├── icon.scss
├── image.scss
├── index.scss
├── infinite-scroll.scss
├── infiniteScroll.scss
├── input-number.scss
├── input.scss
├── link.scss
├── loading.scss
├── main.scss
├── menu-item-group.scss
├── menu-item.scss
├── menu.scss
├── message-box.scss
├── message.scss
├── mixins
│ ├── _button.scss
│ ├── config.scss
│ ├── function.scss
│ ├── mixins.scss
│ └── utils.scss
├── notification.scss
├── option-group.scss
├── option.scss
├── page-header.scss
├── pagination.scss
├── popconfirm.scss
├── popover.scss
├── popper.scss
├── progress.scss
├── radio-button.scss
├── radio-group.scss
├── radio.scss
├── rate.scss
├── reset.scss
├── row.scss
├── scrollbar.scss
├── select-dropdown.scss
├── select.scss
├── slider.scss
├── spinner.scss
├── step.scss
├── steps.scss
├── submenu.scss
├── switch.scss
├── tab-pane.scss
├── table-column.scss
├── table.scss
├── tabs.scss
├── tag.scss
├── time-picker.scss
├── time-select.scss
├── timeline-item.scss
├── timeline.scss
├── tooltip.scss
├── transfer.scss
├── tree.scss
└── upload.scss
在阅读element-ui
中的样式代码时,需要先了解下scss
和BEM
,下面通过element-ui
中实际的代码来介绍下这两个技术点的作用和用途。
1. scss
Sass
是成熟、稳定、强大的CSS预处理器,而SCSS
是Sass3
版本当中引入的新语法特性,完全兼容CSS3
的同时继承了Sass
强大的动态功能。
1.1 变量
在SCSS
中可以将反复使用的CSS
样式定义成一个变量来引用它,无需写重复的CSS
样式,比如我们可以定义一些常用的颜色,字体大小,边框等。如element-ui
源码中,在common
目录下有一个var.scss
文件,定义的全部是一些变量,如果需要修改主题,只需要修改该文件中的变量值就可以了,大大的提升了代码的复用性和可维护性。以下挑了几个很常见的变量,应该不陌生,如下所示:
$--color-primary: #409EFF !default;
/// color|1|Background Color|4
$--color-white: #FFFFFF !default;
/// color|1|Background Color|4
$--color-success: #67C23A !default;
/// color|1|Functional Color|1
$--color-warning: #E6A23C !default;
/// color|1|Functional Color|1
$--color-danger: #F56C6C !default;
/// color|1|Functional Color|1
$--color-info: #909399 !default;
scss
使用$
符号来标识变量,然后再需要使用的地方直接使用既可。如color:$--color-primary
1.2导入scss文件
在SCSS
文件中需要导入其它文件时,直接使用@import
进行导入。如下所示:
@import "mixins/mixins";
@import "common/var";
1.3混合器
单独的使用变量来定义一些重复的代码是远远不够的,因为变量只能简单的定义一些颜色,字体等,而不能定义重复的代码块,这时就得需要混合器
来定义代码块了。混合器使用@mixin
标识符定义,这样就可以轻易地通过引用这个名字重用样式代码了,如element-ui
源码中,在mixin
目录下有一些.scss
文件,定义的全部是一些混合器。如在utils.scss
文件中,有如下一个混合器,不用设置显示成一行的样式:
@mixin utils-ellipsis {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
定义好后,在需要使用的地方使用include
来引用,如下所示:
&.el-checkbox .el-checkbox__label {
width: 100%;
@include utils-ellipsis;
display: block;
box-sizing: border-box;
padding-left: 24px;
line-height: $--transfer-item-height;
}
混合器并不一定总得生成相同的样式。可以通过在@include
混合器时给混合器传参,来定制混合器生成的精确样式。当@include
混合器时,参数其实就是可以赋值给css
属性值的变量,如下所示:
@mixin when($state) {
@at-root {
&.#{$state-prefix + $state} {
@content;
}
}
}
1.4 继承
使用scss
的时候,最后一个减少重复的主要特性就是选择器继承,选择器继承是说一个选择器可以继承为另一个选择器定义的所有样式。这个通过@extend
语法实现。如下所示:
@mixin extend-rule($name) {
@extend #{'%shared-'+$name};
}
1.5 @content
在引用混合样式的时候,可以先将一段代码导入到混合指令中,然后再输出混合样式,额外导入的部分将出现在@content
标志的地方:
@mixin apply-to-ie6-only {
* html {
@content;
}
}
@include apply-to-ie6-only {
#logo {
background-image: url(/logo.gif);
}
}
编译后如下所示:
* html #logo {
background-image: url(/logo.gif);
}
关于@content
的使用,在element-ui
的代码中随处可见,如下所示:
@mixin placeholder {
&::-webkit-input-placeholder {
@content
}
&::-moz-placeholder {
@content
}
&:-ms-input-placeholder {
@content
}
}
2. BEM
BEM
的意思就是块(block)、元素(element)、修饰符(modifier)组成,是由Yandex团队提出的一种CSS Class
命名方法,旨在更好的创建CSS/Sass
模块。BEM
命名具有一定的规范,全名规则为模块名 + 元素名 + 修饰器名。如.el-alert--success
。element-ui
源码中严格遵守了BEM
命名规范,并将块,元素,修饰符单独创建了mixin
,方便复用代码。
@mixin b($block) {
$B: $namespace+'-'+$block !global;
//通过 #{} 插值语句可以在选择器或属性名中使用变量:
.#{$B} {
@content;
}
}
@mixin e($element) {
$E: $element !global;
$selector: &;
$currentSelector: "";
@each $unit in $element {
$currentSelector: #{$currentSelector + "." + $B + $element-separator + $unit + ","};
}
@if hitAllSpecialNestRule($selector) {
@at-root {
#{$selector} {
#{$currentSelector} {
@content;
}
}
}
} @else {
@at-root {
#{$currentSelector} {
@content;
}
}
}
}
@mixin m($modifier) {
$selector: &;
$currentSelector: "";
@each $unit in $modifier {
$currentSelector: #{$currentSelector + & + $modifier-separator + $unit + ","};
}
@at-root {
#{$currentSelector} {
@content;
}
}
}
2.1 块
在BEM
命名规范中,一个块可以当作一个组件,在element-ui
中还引入了命名空间,如alert
组件的命名为:el-alert
。其中el
为命名空间,alert
为组件名。使用命名空间主要是为了防止组件冲突。
2.2 元素
元素是块的子节点,为了表明某个组件的元素,需要在块名后面加上__element
,如element-ui
中的alert
组件,当需要在组件上显示icon
时,可以命名为.el-alert__icon
,让人一看就知道是为alert
组件中的icon
定义的样式。
2.3 修饰符
修饰符是改变某个块的外观的标志。要使用修饰符,可以将 --modifier
添加到块中。如element-ui
中的alert
组件,可以根据不同的颜色来表示不同的状态,有表示成功
、错误
,警告
等不同类型的alert
,不同的类型只需使用不同的样式既可。如:.el-alert--success
,.el-alert--error
等。
3.定义组件样式
了解完scss
和BEM
后,就来了解下element-ui
是如何定义组件样式的。除了date-picker
组件外,其它组件基本上都是一个组件对应一个.scss
文件。这里主要介绍一下alert
组件是如何定义样式的,搞懂了alert
组件后,其它的组件样式就很容易看懂了,都大同小异。
3.1 引入公共样式
首先在文件的最开头,引入mixins
和var
这两个公共样式。
@import "mixins/mixins";
@import "common/var";
3.2 定义块级元素
定义块级元素,就是BEM
规范中的B
,首先来看一下b()
混合器的代码,如下所示:
@mixin b($block) {
$B: $namespace+'-'+$block !global;
.#{$B} {
@content;
}
}
该混合器是传递一个block
变量名,再根据命名空间来生成一个变量,然后再使用.#{$B}
来创建一个class
,比如传入一个alert
,生成后的类名是这样子的。
.el-alert{
@content;
}
类型创建好后,就需要发挥@content;
的作用了,就是将相关的样式复制进来,如下所示调用b
的样式:
@include b(alert) {
width: 100%;
padding: $--alert-padding;
margin: 0;
box-sizing: border-box;
border-radius: $--alert-border-radius;
position: relative;
background-color: $--color-white;
overflow: hidden;
opacity: 1;
display: flex;
align-items: center;
transition: opacity .2s;
}
生成后的代码如下所示:
.el-alert {
width: 100%;
padding: $--alert-padding;
margin: 0;
box-sizing: border-box;
border-radius: $--alert-border-radius;
position: relative;
background-color: $--color-white;
overflow: hidden;
opacity: 1;
display: flex;
align-items: center;
transition: opacity .2s;
}
3.3 定义子级元素
定义子级元素,就是BEM
规范中的E
。e
混合器的代码如下所示:
@mixin e($element) {
$E: $element !global;
$selector: &;
$currentSelector: "";
@each $unit in $element {
$currentSelector: #{$currentSelector + "." + $B + $element-separator + $unit + ","};
}
@if hitAllSpecialNestRule($selector) {
@at-root {
#{$selector} {
#{$currentSelector} {
@content;
}
}
}
} @else {
@at-root {
#{$currentSelector} {
@content;
}
}
}
}
这里,需要先搞懂each
和at-root
的用法。
@each
的作用是将$element
中的元素名遍历出来赋值给$unit
变量。然后再生成类似el-alert__xx
的class
名称。
@at-root
指令可以使一个或多个规则被限定输出在文档的根层级上,而不是被嵌套在其父选择器下。
hitAllSpecialNestRule
用于判断类型是否为修饰符样式
,标记样式
,伪类样式
中的一种。如果是其中的一种,需要将其嵌套在&
下。如下所示为icon
的样式。
@include e(icon) {
font-size: $--alert-icon-size;
width: $--alert-icon-size;
@include when(big) {
font-size: $--alert-icon-large-size;
width: $--alert-icon-large-size;
}
}
3.4 定义修饰符
定义子级元素,就是BEM
规范中的M
。m
混合器的代码如下所示:
@mixin m($modifier) {
$selector: &;
$currentSelector: "";
@each $unit in $modifier {
$currentSelector: #{$currentSelector + & + $modifier-separator + $unit + ","};
}
@at-root {
#{$currentSelector} {
@content;
}
}
}
搞懂e
混合器的实现原理后,看m
混合器的实现就更简单了,就是先遍历修饰符,然后再生成具有修饰符的class
名。alert
中定义的success
类型如下所示:
@include m(success) {
&.is-light {
background-color: $--alert-success-color;
color: $--color-success;
.el-alert__description {
color: $--color-success;
}
}
&.is-dark {
background-color: $--color-success;
color: $--color-white;
}
}
总结
样式的定义基本上告一段落了,主要是活用mixins
中的混合器和BEM
规范,平时多写一些公共样式基本就能熟练相关特性了。
网友评论