前言
团队要依据新的 UI 标准实现一个组件库,button
组件标准要反对多种主题换肤,字体色彩、背景色彩、边框和禁用应用新的标准,并且一种主题色彩次要组件上应用两种主题颜色混合。另外,减少多一种幽灵按钮类型,通过剖析,在 element-ui 的 button
组件上革新麻烦,不好保护,所以须要造一个 button 组件,浏览 element-ui 组件库 button
的源码设计,参考 element-plus css 自定义变量
实现
element-ui 源码剖析 button
button 属性
button文档属性 能够定义按钮的尺寸(size),类型(type),奢侈款式(plain),圆角(round),圆形(circle),加载(loading),禁用(disabled),图标(icon),是否聚焦(autofocus)等
参数 | 阐明 | 类型 | 可选值 | 默认值 |
---|---|---|---|---|
size | 尺寸 | string | medium / small / mini | — |
type | 类型 | string | primary / success / warning / danger / info / text | — |
plain | 是否奢侈按钮 | boolean | — | false |
round | 是否圆角按钮 | boolean | — | false |
circle | 是否圆形按钮 | boolean | — | false |
loading | 是否加载中状态 | boolean | — | false |
disabled | 是否禁用状态 | boolean | — | false |
icon | 图标类名 | string | — | — |
autofocus | 是否默认聚焦 | boolean | — | false |
native-type | 原生 type 属性 | string | button / submit / reset | button |
button 应用
<template> <div> <el-button>默认按钮</el-button> <!-- 尺寸(size):medium(中等),small(小型),mini(超小) --> <el-button size="medium">中等按钮</el-button> <!-- 类型(type):default(默认),primary(次要),success(胜利),info(信息),warning(正告),danger(危险),text(文本) --> <el-button type="primary">次要按钮</el-button> <el-button type="text">文字按钮</el-button> <!-- 奢侈(plain):true,false --> <el-button type="primary" plain>次要按钮</el-button> <!-- 圆角(round):true,false --> <el-button round>次要按钮</el-button> <!-- 圆形(circle):true,false。个别与图标 icon 连用 --> <el-button icon="el-icon-search" circle></el-button> <!-- 禁用(disabled):true,false。按钮色彩变浅,鼠标悬浮呈现禁止符号。 --> <el-button disabled>禁用按钮</el-button> <!-- 图标(icon):设置icon属性即可,能够参考 icon 组件。设置在文字左边的 icon ,须要应用i标签,此时最好增加上 el-icon--right 类。 --> <el-button type="primary" icon="el-icon-edit">图标按钮</el-button> <el-button type="primary" >图标按钮<i class="el-icon-edit el-icon--right"></i ></el-button> <!-- 加载(loading):true,false。为 true 时点击事件生效。 --> <el-button type="primary" :loading="true">加载中</el-button> </div></template>
Button 源码剖析
相干文件门路:
- packages/button/src/button.vue button vue 单文件组件
- packages/theme-chalk/src/common/var.scss#L506-L591 按钮 SCSS 变量名文件
- packages/theme-chalk/button.scss 按钮款式文件
- packages/theme-chalk/mixins/_button.scss 按钮混入款式文件,公共混入代码,例如按钮不同
type
款式、按钮plain
奢侈款式、按钮size
大小款式 - packages/theme-chalk/minxins.scss 混入款式文件, 包含
bem
标准、when
状态 mixin 代码片段,例如b(button)
会转化为el-button
button.vue 文件
button.vue文件门路
props
的属性在文档阐明中都有提到,是对组件的扩大
type
: 拼接在el-button--
前面,生成不同的class
类,从新定义color
、background-color
、border-color
笼罩el-button
默认款式size
:内部管制按钮大小,同时能够被表单元素和全局管制,el-button--
+ size 类款式,例如el-button--small
icon
:反对不同的图标,加载中的图标只能应用el-icon-loading
nativeType
:按钮的原生类型,默认是button
,能够是submit
、reset
等loading
:加载中的状态,is-loading
款式,按钮会被禁用disabled
:禁用按钮,is-disabled
类款式,应用when(disabled)
生成plain
:奢侈按钮,is-plain
类款式autofocus
:主动聚焦,focus
状态款式按钮round
:圆角款式is-round
circle
:圆心款式is-circle
,个别配合icon
图标应用
// button.vue 源码<template> <!-- 原生的 button 进行款式封装。 --> <button class="el-button" @click="handleClick" :disabled="buttonDisabled || loading" :autofocus="autofocus" :type="nativeType" :class="[ type ? 'el-button--' + type : '', buttonSize ? 'el-button--' + buttonSize : '', { 'is-disabled': buttonDisabled, 'is-loading': loading, 'is-plain': plain, 'is-round': round, 'is-circle': circle } ]" > <!-- 1. el-icon-loading 是加载图标类, 2. 这里应用了两个 <i></i> 都是用来放图标的,会依据 loading 的值进行二选一 并且 loading = ture 加载状态无奈应用其余图标,同时外层 button 被设置为 disabled --> <i class="el-icon-loading" v-if="loading"></i> <i :class="icon" v-if="icon && !loading"></i> <!-- 按钮文本插槽,default 属性包含了所有没有被蕴含在具名插槽中的节点 --> <span v-if="$slots.default"><slot></slot></span> </button></template>
SCSS 变量文件
common/var.scss 公共变量文件源码 定义公共款式和所有组件款式变量的文件,例如主题色彩、字体色彩、字体大小等,能够通过这个文件实现组件库的换肤
例如主题变量 $--color-primary
mix
函数是将两种色彩按不同的占比混合生成一个新的色彩,例如 mix($--color-white, $--color-primary, 10%)
, $--color-white
色彩占比 10%,$--color-primary
占比 90%,生成一种新的色彩
$--color-primary: #409EFF !default;/// color|1|Background Color|4$--color-white: #FFFFFF !default;/// color|1|Background Color|4$--color-black: #000000 !default;$--color-primary-light-1: mix($--color-white, $--color-primary, 10%) !default; /* 53a8ff */$--color-primary-light-2: mix($--color-white, $--color-primary, 20%) !default; /* 66b1ff */$--color-primary-light-3: mix($--color-white, $--color-primary, 30%) !default; /* 79bbff */$--color-primary-light-4: mix($--color-white, $--color-primary, 40%) !default; /* 8cc5ff */$--color-primary-light-5: mix($--color-white, $--color-primary, 50%) !default; /* a0cfff */$--color-primary-light-6: mix($--color-white, $--color-primary, 60%) !default; /* b3d8ff */$--color-primary-light-7: mix($--color-white, $--color-primary, 70%) !default; /* c6e2ff */$--color-primary-light-8: mix($--color-white, $--color-primary, 80%) !default; /* d9ecff */$--color-primary-light-9: mix($--color-white, $--color-primary, 90%) !default;
BEM CSS标准
element 的款式标准是应用 bem 治理,依据标准生成类名,防止款式净化,bem 函数的公共代码片段定义在 packages/theme-chalk/src/mixins/mixins.scss 文件
theme-chalk/src/mixins/config.scss 定义命名空间
$namespace: 'el';$element-separator: '__';$modifier-separator: '--';$state-prefix: 'is-';
1、b
是 block
的 mixin简写函数,调用 @include b(button)
参数 $block
赋值 button
, 拼接命名空间变量 $namespace
el 失去 el-button
, !global
改为全局变量,能够给下文应用, @content
占位符插入内容
@mixin b($block) { $B: $namespace+'-'+$block !global; .#{$B} { @content; }}
应用 @mixin b
代码片段
@include b(button) { display: inline-block; line-height: 1;}
编译后果是
.el-button { display: inline-block; line-height: 1;}
2、e
是 element 的简写函数,@include e(icon)
调用,$element
传入 icon,在下面 b
函数曾经将 $B
赋值为全局变量 el-button
,$currentSelector
拼接后失去 .el-button__icon
,@at-root 是跳出嵌套,和 .el-button
同级,而不是 .el-button .el-button-icon
拼接在前面
@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; } } }}
3、m
修饰符函数,传入 primary
, 遍历 $modifier
只有一个元素,遍历后果后 $currentSelector
赋值是 &--primary
,在scss 编译, & 是下级类 el-button
,编辑是 el-button--primary
@mixin m($modifier) { $selector: &; $currentSelector: ""; @each $unit in $modifier { $currentSelector: #{$currentSelector + & + $modifier-separator + $unit + ","}; } @at-root { #{$currentSelector} { @content; } }}
应用 m
函数
@include b(button) { @include m(primary) { }}
编译后果是
.el-button--primary { }
button.scss 组件款式
组件库的款式独自独自放在一个目录治理,通过 gulp
打包,源码门路 packages/theme-chalk/src/button.scss
@include b(button)
定义 el-button
类款式,& + &
相邻两个按钮左间距 10px
,button-size
是生成按钮的大小,设计程度、垂直的内边距,字体大小和边框圆角,形象出办法定义在 packages/theme-chalk/src/mixins/_button
@include b(button) { // 根本款式,默认款式,在未指定 type 之前 display: inline-block; line-height: 1; white-space: nowrap; cursor: pointer; background: $--button-default-background-color; // 默认背景色, white border: $--border-base; // 1px solid #DCDFE6 border-color: $--button-default-border-color; // #DCDFE6 color: $--button-default-font-color; // #606266 -webkit-appearance: none; text-align: center; box-sizing: border-box; outline: none; margin: 0; transition: .1s; font-weight: $--button-font-weight; // 500 // 在 packages/theme-chalk/src/mixins/utils ,次要是 moz,webkit,ms 的用户抉择设置 @include utils-user-select(none); // 兄弟节点之间 & + & { margin-left: 10px; } // 在 packages/theme-chalk/src/mixins/_button,设置按钮边距、字体、边框弧度 @include button-size($--button-padding-vertical, $--button-padding-horizontal, $--button-font-size, $--button-border-radius); // 悬浮、聚焦按钮款式 &:hover, &:focus { color: $--color-primary; border-color: $--color-primary-light-7; background-color: $--color-primary-light-9; } &::-moz-focus-inner { border: 0; } // 按钮图标和文字的间距 & [class*="el-icon-"] { & + span { margin-left: 5px; } } ...}
when 函数 是定义不同状态的款式,$state-prefix
是 is-
,传入状态类型拼接,例如 when(loading)
是 .el-button.is-loading
@mixin when($state) { @at-root { &.#{$state-prefix + $state} { @content; } }}
when(plain)
、when(active)
、when(disabled)
、when(loading)
、when(round)
、when(circle)
别离定义 is-plain
,is-active
、is-disabled
、is-loading
、is-round
、is-circle
,这也是 button prop
传入的属性,生成不同的类款式显示
@include when(plain) { &:hover, &:focus { background: $--color-white; border-color: $--color-primary; color: $--color-primary; } &:active { background: $--color-white; border-color: mix($--color-black, $--color-primary, $--button-active-shade-percent); color: mix($--color-black, $--color-primary, $--button-active-shade-percent); outline: none; } } @include when(active) { color: mix($--color-black, $--color-primary, $--button-active-shade-percent); border-color: mix($--color-black, $--color-primary, $--button-active-shade-percent); } @include when(disabled) { &, &:hover, &:focus { color: $--button-disabled-font-color; cursor: not-allowed; background-image: none; background-color: $--button-disabled-background-color; border-color: $--button-disabled-border-color; } &.el-button--text { background-color: transparent; } &.is-plain { &, &:hover, &:focus { background-color: $--color-white; border-color: $--button-disabled-border-color; color: $--button-disabled-font-color; } } } @include when(loading) { position: relative; pointer-events: none; &:before { pointer-events: none; content: ''; position: absolute; left: -1px; top: -1px; right: -1px; bottom: -1px; border-radius: inherit; background-color: rgba(255,255,255,.35); } } @include when(round) { border-radius: 20px; padding: 12px 23px; } @include when(circle) { border-radius: 50%; padding: $--button-padding-vertical; }
例如 is-plain
款式
组件传入 type
,是通过上面 m()
函数定义不同的 class,button-variant
代码片段是定义在 mixins/_button,
@include m(primary) { @include button-variant($--button-primary-font-color, $--button-primary-background-color, $--button-primary-border-color); } @include m(success) { @include button-variant($--button-success-font-color, $--button-success-background-color, $--button-success-border-color); } @include m(warning) { @include button-variant($--button-warning-font-color, $--button-warning-background-color, $--button-warning-border-color); } @include m(danger) { @include button-variant($--button-danger-font-color, $--button-danger-background-color, $--button-danger-border-color); } @include m(info) { @include button-variant($--button-info-font-color, $--button-info-background-color, $--button-info-border-color); }
button-variant
传入不同的 color
、background-color
、border-color
变量笼罩 default
默认按钮的字体色彩、背景色彩和边框色彩,并且定义了伪类 hover
、focus
、active
、disabled
交互状态,色彩变浅通过 mix
和 红色
混合
@mixin button-variant($color, $background-color, $border-color) { color: $color; background-color: $background-color; border-color: $border-color; &:hover, &:focus { background: mix($--color-white, $background-color, $--button-hover-tint-percent); border-color: mix($--color-white, $border-color, $--button-hover-tint-percent); color: $color; } &:active { background: mix($--color-black, $background-color, $--button-active-shade-percent); border-color: mix($--color-black, $border-color, $--button-active-shade-percent); color: $color; outline: none; } &.is-active { background: mix($--color-black, $background-color, $--button-active-shade-percent); border-color: mix($--color-black, $border-color, $--button-active-shade-percent); color: $color; } &.is-disabled { &, &:hover, &:focus, &:active { color: $--color-white; background-color: mix($background-color, $--color-white); border-color: mix($border-color, $--color-white); } } &.is-plain { @include button-plain($background-color); }}
&.is-plain
类笼罩次要按钮的色彩失去奢侈按钮,应用 button-plain($background-color)
定义在同一个文件,伪类也是定义 color
、background-color
、border-color
笼罩
@mixin button-plain($color) { color: $color; background: mix($--color-white, $color, 90%); border-color: mix($--color-white, $color, 60%); &:hover, &:focus { background: $color; border-color: $color; color: $--color-white; } &:active { background: mix($--color-black, $color, $--button-active-shade-percent); border-color: mix($--color-black, $color, $--button-active-shade-percent); color: $--color-white; outline: none; } &.is-disabled { &, &:hover, &:focus, &:active { color: mix($--color-white, $color, 40%); background-color: mix($--color-white, $color, 90%); border-color: mix($--color-white, $color, 80%); } }}
button
提供 4 种按钮大小,默认是最大的按钮,还有 medium
中等、small
小的和 mini
特小的,它是调用 button-size
传入不同的垂直、程度的内间距、字体大小和边框圆角变量值,定义按钮的大小
@include m(medium) { @include button-size($--button-medium-padding-vertical, $--button-medium-padding-horizontal, $--button-medium-font-size, $--button-medium-border-radius); @include when(circle) { padding: $--button-medium-padding-vertical; } } @include m(small) { @include button-size($--button-small-padding-vertical, $--button-small-padding-horizontal, $--button-small-font-size, $--button-small-border-radius); @include when(circle) { padding: $--button-small-padding-vertical; } } @include m(mini) { @include button-size($--button-mini-padding-vertical, $--button-mini-padding-horizontal, $--button-mini-font-size, $--button-mini-border-radius); @include when(circle) { padding: $--button-mini-padding-vertical; } }
button-size
代码定义在 mixin/_button.scss
@mixin button-size($padding-vertical, $padding-horizontal, $font-size, $border-radius) { padding: $padding-vertical $padding-horizontal; font-size: $font-size; border-radius: $border-radius; &.is-round { padding: $padding-vertical $padding-horizontal; }}
按钮反对文本类型 el-button--text
,将 border
、background
变通明,设置不同的字体色彩,定义伪类状态
@include m(text) { border-color: transparent; color: $--color-primary; background: transparent; padding-left: 0; padding-right: 0; &:hover, &:focus { color: mix($--color-white, $--color-primary, $--button-hover-tint-percent); border-color: transparent; background-color: transparent; } &:active { color: mix($--color-black, $--color-primary, $--button-active-shade-percent); border-color: transparent; background-color: transparent; } &.is-disabled, &.is-disabled:hover, &.is-disabled:focus { border-color: transparent; } }}
总结一下,button
按钮的款式变量定义在 commont/var.scss
保护,其余组件也是这种做法,这样就做到只须要批改 var.scss
文件就能够实现组件库的换肤。
按钮的款式标准应用 bem
标准,@include b(button)
定义根底类款式 el-button
;按钮大小是通过 @include button-size(...)
传入内边距、字体大小变量管制显示。
不同的按钮 type
类型、伪类状态还有奢侈按钮,通过批改 color
、background-color
、border-color
笼罩默认款式,色彩变浅通过 mix
函数混合红色生成新的色彩,文本按钮、按钮组还有不同的按钮状态依据 bem
标准生成类款式定义。
通过学习优良的组件库设计,发现处处设计的很奇妙,站在伟人的肩膀上学习。
本文由mdnice多平台公布