前言
为锻炼封装组件得能力,我打算研究Elment UI组件,学习人家的设计和代码。
本文的研究思路是通过阅读Element源码,然后自动动手一步一步编写组件,完善其对应功能。
第一个想研究的就是Button组件,是我们最常用也最熟悉的,对标官方文档中介绍的Button特性,我们一个个来实现它们。
准备工作
使用vue-cli新建一个空项目
vue create my-element-ui
在项目中的components文件夹下新建Button文件夹,再新建一个index.vue,写按钮组件的代码。在根目录下新建views文件夹,再新建ButtonShownPage文件夹,再新建index.vue,在此页面展示按钮组件。
在ButtonShownPage中我写下
<template>
<el-button @click="handleClick">默认按钮</el-button>
</template>
<script>
import ElButton from '../../components/Button/index'
export default {
name: 'ButtonShownPage',
methods: {
handleClick(evt) {
console.log('handleClick', evt);
}
},
components: {
ElButton
}
}
</script>
这样我们开始专心写Button的代码,使其有正确的呈现
编写基本的Button
经过简单的摸索,我在Button组件里写下如下代码:
<template>
<button
class="el-button"
@click="handleClick"
>
<span v-if="$slots.default"><slot></slot></span>
</button>
</template>
<script>
export default {
name: 'Button',
methods: {
handleClick(evt) {
this.$emit('click', evt);
}
}
}
</script>
<style>
.el-button {
display: inline-block;
line-height: 1;
white-space: nowrap;
cursor: pointer;
background: #fff;
border: 1px solid #dcdfe6;
color: #606266;
-webkit-appearance: none;
text-align: center;
box-sizing: border-box;
outline: none;
margin: 0;
transition: .1s;
font-weight: 500;
-moz-user-select: none;
-webkit-user-select: none;
-ms-user-select: none;
padding: 12px 20px;
font-size: 14px;
border-radius: 4px;
}
.el-button:focus, .el-button:hover {
color: #409eff;
border-color: #c6e2ff;
background-color: #ecf5ff;
}
.el-button:active {
color:#3a8ee6;
border-color:#3a8ee6;
outline:none
}
</style>
使用button标签作为组件的基础,编写包含默认、focus、hover、active几种状态下的样式。利用slot包含按钮内容,click事件暴露出去。最基本的按钮就有了。
增加类型
现在让我们的button支持type,分别是primary,success,info,warning,danger。
先在ButtonShownPage中写测试代码:
<el-button @click="handleClick">默认按钮</el-button>
<el-button type="primary">主要按钮</el-button>
<el-button type="success">成功按钮</el-button>
<el-button type="info">信息按钮</el-button>
<el-button type="warning">警告按钮</el-button>
<el-button type="danger">危险按钮</el-button>
再在Button中写逻辑代码:
<template>
<button
class="el-button"
:class="[type ? 'el-button--' + type : '']"
@click="handleClick"
>
<span v-if="$slots.default"><slot></slot></span>
</button>
</template>
<script>
export default {
name: 'Button',
props: {
type: {
type: String,
default: 'default',
},
},
methods: {
handleClick(evt) {
this.$emit('click', evt);
}
}
}
</script>
加一个prop名为type,默认为default,根据传入的值,为button标签添加样式。如传入的是primary,那么button就有了.el-button–primary这个class,我们再添加样式:
.el-button--primary {
color: #fff;
background-color: #409eff;
border-color: #409eff;
}
.el-button--primary:focus, .el-button--primary:hover {
background: #66b1ff;
border-color: #66b1ff;
color: #fff;
}
功能完成,另外添加个这样的样式:
.el-button+.el-button {
margin-left: 10px;
}
即可为挨着的按钮们增加一个间距,效果如图:
plain、round和circle
这三个同type如出一辙,都是通过添加class来控制样式
<template>
<button
class="el-button"
:class="[type ? 'el-button--' + type : '',
{
'is-plain': plain,
'is-round': round,
'is-circle': circle
}]"
@click="handleClick"
>
<span v-if="$slots.default"><slot></slot></span>
</button>
</template>
这时在props里增加plain, round, circle属性。
props: {
type: {
type: String,
default: 'default',
},
plain: Boolean,
round: Boolean,
circle: Boolean
},
由于要引用样式太多了,原理又相同,我通过浏览器开发者工具直接将elementui的样式直接整个copy过来放在assets文件夹中。
这样,效果如下:
我们的按钮支持多种形态了。
这里我们将所有的elemnnt的样式都拷贝过来了,所以后续就不需要关心样式的编写,只关心标签和js的逻辑,即可实现相应功能,但可以在呈现结果中,利用浏览器工具继续研究样式的细节。
支持icon
新增一个Icon组件:
<template>
<i :class="'el-icon-' + name"></i>
</template>
<script>
export default {
name: 'ElIcon',
props: {
name: String
}
};
</script>
将Icon组件引入Button内,
<template>
<button
class="el-button"
:class="[type ? 'el-button--' + type : '',
{
'is-plain': plain,
'is-round': round,
'is-circle': circle
}]"
@click="handleClick"
>
<el-i :class="icon" v-if="icon"></el-i>
<span v-if="$slots.default"><slot></slot></span>
</button>
</template>
这样就达到了效果:
禁用状态
- 给组件增加一个属性disabled
- 给button标签的属性disabled绑定组件disabled属性,确保点击事件不生效
- 在:class中加一条’is-disabled’: disabled即可。
效果如下:
文字按钮,图标按钮
现在我们的组件已经支持这两种样式的按钮了。
按钮组
新增一个ButtonGroup组件,关键点还是在于控制样式
<template>
<div class="el-button-group">
<slot></slot>
</div>
</template>
<script>
export default {
name: 'ElButtonGroup'
};
</script>
使用如下:
<el-button-group>
<el-button type="primary" icon="el-icon-arrow-left">上一页</el-button>
<el-button type="primary">下一页<i class="el-icon-arrow-right el-icon--right"></i></el-button>
</el-button-group>
呈现效果如下:
加载中
- 给组件增加一个属性loading
- 给class增加一条’is-loading’: loading, 把disabled变成:disabled=“disabled || loading”
- 把 <el-i :class=“icon” v-if="icon></el-i>变成
<el-i class="el-icon-loading" v-if="loading"></el-i>
<el-i :class="icon" v-if="icon && !loading"></el-i>
效果如下:
按钮大小
同type一个套路,加一个size属性,控制class。
总结
基本的按钮组件就研究到这。全部示例如下:
本文的所有代码已上传至码云:https://gitee.com/DaBuChen/my-element-ui/tree/button/
其他组件源码研究:
Element组件源码研究-Layout,Link,Radio
版权声明:著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。 作者: 猎户座小陈 原文链接:https://juejin.im/post/6854573208255856648