“ 本文正在参加「金石计划 . 瓜分6万现金大奖」”
一 button概述
button-group和index.js文件比较简单,这里重点我们分析button.vue文件
二 button源码分析
重点部分我会在代码中加上注释:
<template>
<!-- button的结构部分 可以看出element-ui的button按钮样式在原生的button上添加属性方法样式 -->
<!-- 这里的class类没有直接写在此vue文件中,后面分析 -->
<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
}
]"
>
<!-- 加载图标 这里是二选一 加载图标和普通图标只能有一个-->
<i class="el-icon-loading" v-if="loading"></i>
<i :class="icon" v-if="icon && !loading"></i>
<!-- 默认文字/button中的文字 文本插槽 -->
<span v-if="$slots.default"><slot></slot></span>
</button>
</template>
<script>
export default {
name: 'ElButton',
inject: {
elForm: {
default: ''
},
elFormItem: {
default: ''
}
},
// props就是我们平时在业务中使用button时设置的参数,这里与官网的文档参数一一对应
props: {
type: { //类型
type: String,
default: 'default'
},
size: String,
icon: {
type: String,
default: ''
},
nativeType: { //原生type属性
type: String,
default: 'button'
},
loading: Boolean, //加载状态
disabled: Boolean, // 是否禁用
plain: Boolean, // 是否为朴素按钮
autofocus: Boolean,// 默认聚焦
round: Boolean, //是否为圆角按钮
circle: Boolean //是否为原型按钮
},
// 此处是重点 到下面分析
computed: {
_elFormItemSize() {
return (this.elFormItem || {}).elFormItemSize;
},
buttonSize() {
return this.size || this._elFormItemSize || (this.$ELEMENT || {}).size;
},
buttonDisabled() {
return this.$options.propsData.hasOwnProperty('disabled') ? this.disabled : (this.elForm || {}).disabled;
}
},
methods: {
handleClick(evt) {
this.$emit('click', evt);
}
}
};
</script>
三 button样式代码分析
通过分析源码发现,样式代码并没有直接写在vue文件中,全局搜索后发现
package/theme-chalk中找到button按钮的样式文件
查看其他组件的vue文件发现也没有样式代码,所以element中的样式代码时单独放到这里的。
先从import的几个文件,挨个分析
var.scss
common/var.scss 公共变量文件源码 定义公共样式和所有组件样式变量的文件,例如主题颜色、字体颜色、字体大小等,可以通过这个文件实现组件库的换肤。 其中有一个写法特别感兴趣:
/* Color
-------------------------- */
/// color|1|Brand Color|0
$--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; /* ecf5ff */
/// color|1|Functional Color|1
$--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;
这种mix的样式写法是把两种颜色按照不同比例混合而成生成新的颜色,百分比则是占比,可以混合多种颜色 很有趣
mix详解MDN链接 其他的没什么好讲的,用的是scss语法,太久没用了很多属性忘记,留个坑后续把这块补上。
_button.scss
打开后发现主要封装了三种样式,那为什么不在button中直接写呢,然后继续找到其中一个方法全局查找
根据 button-size全局查找后得出一下结果
可以看出来由于这三个方法在很多地方用到,所以用也是单独拿出来写
在其他地方用到的写法:
@include button-size($--button-padding-vertical, $--button-padding-horizontal, $--button-font-size, $--button-border-radius);
三个方法分别为: button-plain:朴素样式 button-variant:不同类型的按钮样式 button-size:按钮尺寸样式 注:传入的参数$color是从var.scss中传入的。这也是可以全局换肤的原因 这里我就单独那应该出来解释,因为他们写法多差不多,挑最难的一个
<!-- 导入三个参数, -->
@mixin button-variant($color, $background-color, $border-color) {
color: $color;
background-color: $background-color;
border-color: $border-color;
&:hover,
&:focus {
// 使用mix混合颜色,上面讲过
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);
}
}
// 背景使用plain(朴素)时的背景
&.is-plain {
@include button-plain($background-color);
}
}
mixins.scss
这个文件夹,写的应该是一些公共样式,只能在后续的学习中遇到再具体分析,但是遇到几个不理解的点还是学习一下: 下面这段代码吸引了我的注意: 1 BEM是什么? 2 别的样式命名都是mixin+单词,这里使用字母 b e m是什么意思?难道因为简单吗?
/* BEM
-------------------------- */
@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;
}
}
}
那么就来研究一手:
百度百科得知:
BEM是一种前端命名方法论,主要是针对CSS,意思是块(Block)、元素(Element)、修饰符(Modifier)的简写。这种命名方法让CSS便于统一团队开发规范和方便维护。
换句话说 b是block的写法,e是element m是modifiter , 通过多方操作后得到大佬解读:
2、e 是 element 的简写函数,@include e(icon) 调用,B 赋值为全局变量 el-button,$currentSelector 拼接后得到 .el-button__icon,@at-root 是跳出嵌套,和 .el-button 同级,而不是 .el-button .el-button-icon 拼接在后面
3、m 修饰符函数,传入 primary, 遍历 currentSelector 赋值是 &--primary,在scss 编译, & 是上级类 el-button,编辑是 el-button--primary 举例: 在_mixins.scss中使用:
@mixin b($block) {
$B: $namespace+'-'+$block !global;
.#{$B} {
@content;
}
}
element大佬在源码中使用:
@include b(button) {
display: inline-block;
line-height: 1;
}
编译结果:
.el-button {
display: inline-block;
line-height: 1;
}
utils.scss
这个文件和上面的几个差不多,也是一些公共样式,应该为了便于维护
四 button逻辑代码分析
computed: {
_elFormItemSize() {
return (this.elFormItem || {}).elFormItemSize;
},
buttonSize() {
return this.size || this._elFormItemSize || (this.$ELEMENT || {}).size;
},
buttonDisabled() {
return this.$options.propsData.hasOwnProperty('disabled') ? this.disabled : (this.elForm || {}).disabled;
}
}
主要有三个计算属性
其实在button中的数据,有两个来源,一个是普通的prop引入的,这个比较好理解,参数都是依靠这个引入的,
另外一个就是computed计算属性了
这里就涉及到provider/inject,
provider/inject:简单的来说就是在父组件中通过provider来提供变量,然后在子组件中通过inject来注入变量。注意官方文档有一句话“向其所有子孙后代注入一个依赖,不论组件层次有多深,并在起上下游关系成立的时间里始终生效”,这样看来就明显了,父组件提供的属性只能在直接子组件中获取到,孙组件就获取不到,provide提供的属性就解决了这个问题。 为什么用到computed?答案就很明显了,防止全局设置/父组件的属性,孙组件获取不到
Vue.use(Element, { size: Cookies.get('size') || 'medium' // set element-ui default size })
有了provider/inject,这里配置的size才能全局起作用。
在这里分别监听的button的大小,是否禁用,以及formItem的大小,从而做出改变。
至于methods中的cilck事件 就比较简单,
子组件向父组件通信,使用了$emit事件。
methods: {
handleClick(evt) {
this.$emit('click', evt);
}
}
本篇着重对于button的一些样式代码进行了分析,因为逻辑部分确实比较简单,刚刚开始,后面会选择一些典型的组件,继续去阅读源码学习。
以上就是Button按钮的源码分析过程,刚刚开始学习,文章中若有不足或者错误的地方!欢迎指正