本篇文章记录仿写一个
el-button
组件细节,从而有助于大家更好了解饿了么ui对应组件具体工作细节。本文是elementui源码学习仿写系列的又一篇文章,后续闲暇了会不断更新并仿写其余组件。源码在github上,大家能够拉下来,npm start运行跑起来,联合正文有助于更好的了解网站成果演示:http://ashuai.work:8888/#/myB...
GitHub仓库地址:https://github.com/shuirongsh...
什么是Button组件
按钮用于点击,个别是做事件的响应。
按钮封装效果图
按钮分类
繁多按钮
- 默认按钮
- 主题按钮(primary、success、warning、error)
- 按钮大小(small、middle、big)
- 按钮禁用(disabled)
- 按钮加载(loading)
- 按钮的图标地位(默认图标在按钮文字左侧)
- 图标按钮(没有按钮文字)
- 繁多文字按钮
- 按钮组(按钮组中有多个按钮)
默认按钮
默认按钮很简略,只是写一个最一般的款式即可
<button :class="[ 'myButton' ]" />
.myButton { display: inline-flex; align-items: center; justify-content: center; white-space: nowrap; box-sizing: border-box; padding: 12px 16px; background-color: rgba(0, 0, 0, 0.1); color: #222; border: none; cursor: pointer; user-select: none; // 不让选中文字 transition: all 0.3s; font-size: 14px;}// 悬浮成果.myButton:hover { background-color: rgba(0, 0, 0, 0.2);}// 按中成果.myButton:active { background-color: rgba(0, 0, 0, 0.3);}
笔者这里是将悬浮的成果和按中的成果,设置背景色越来越深。这样的话,看着成果比拟显著
主题按钮
所谓按钮的主题,就是增加不同的类名,比方primary
主题的按钮,就加上.primary
类名、success
主题的按钮,就加上.success
类名。而后应用动静class
去增加即可(这里应用动静class的数组用法)。如:
<button :class="[ 'myButton', type ]" />
变量type
的值源自于应用按钮组件时,传递进来的type
参数
const typeArr = [ "", "primary", "success", "warning", "error", "text", "dangerText",];props:{ type: { // 按钮主题类型 type: String, validator(val) { return typeArr.includes(val); // 这里能够加一个校验函数,其实不加也行 }, },}
而后给不同type值加上对应的款式即可。如下:
// primary款式.primary { background-color: #1867c0; color: #fff;}.primary:hover { background-color: #0854ac;}.primary:active { background-color: #033d7f;}// success款式.success { background-color: #19be6b; color: #fff;}.success:hover { background-color: #0ea459;}.success:active { background-color: #008140;}// warning款式.warning { background-color: #ffc163; color: #fff;}.warning:hover { background-color: #db952d;}.warning:active { background-color: #b97b1d;}// 等等type值款式...
按钮大小
按钮大小能够应用padding
值的大小去管制,也能够间接应用zoom
缩放做管制
这里应用动静style
搭配计算属性的形式去管制,如下代码:
// 不同的大小指定不同的缩放水平const sizeObj = { small: 0.85, middle: 1, big: 1.2,};props:{ size: String }<button :style="styleCal" />computed: { styleCal() { return { zoom: sizeObj[this.size] // zoom缩放的值大小取决于传递进来的size值 } }}
按钮禁用
按钮禁用disable
没啥好说的,次要是要留神loading
的时候,也要禁用掉,loading
加载的时候,不容许用户再点击。
<button :disabled="disabled || loading" />
props:{ loading:Boolean}
这里留神一下,按钮禁用的款式也是通过动静class加上的,请往下看
按钮加载
留神加载时款式和加载按钮图标进去的时候,将其余的图标给暗藏起来。(同一时刻,只能有一个按钮图标,这样保障按钮加载时简洁一些)
<button :class="[ 'myButton', // 默认款式 disabled ? 'disabledBtn' : '', // 动静加上禁用按钮款式 loading ? 'loadingBtn' : '', // 动静加上loading加载中按钮款式 type, // 主题款式 ]" :disabled="disabled || loading" // 禁用时禁用,加载时也禁用 > <i class="el-icon-loading" v-if="loading"></i> <!-- 应用传进来的图标,通过动静style管制图标和文字见的距离,同一时刻下, 只能有一个图标呈现,所以有loading图标了,就不能有别的图标了 --> <i :class="icon" :style="styleGap" v-if="icon && !loading"></i> <slot></slot> </button>
按钮的图标地位
默认从左往右排列(图标在左侧、文字在右侧),这里咱们能够应用弹性盒的方向flexDirection
属性,来管制从左往右还是从右往左排列
<button :style="styleCal"/>styleCal() { // 管制缩放和指定默认圆角以及设置图标在文字左侧还是右侧 let styleObj = { zoom: sizeObj[this.size], borderRadius: "5px", flexDirection: this.rightIcon ? "row-reverse" : "row", }; return styleObj;},
图标按钮和繁多文字按钮
这两个也很简略,
- 图标按钮留神加圆角的机会
- 繁多文字按钮的款式要预留设置一份即可
而后动态控制一下即可
按钮组
按钮组注意事项:
- 首先将所有的按钮的圆角全副去掉(这样的话,所有的按钮都是方方正正的按钮了)
- 而后独自给第一个按钮
:first-of-type
的左上角和左下角的圆角设置一下 - 而后再给最初一个按钮
last-of-type
的右上角和右下角的圆角设置一下 - 最初,按钮组之间须要有距离,这里应用
border-right
做分割线 - 最最初,再把最初一个按钮的左边框去掉即可,如下css代码
// 附上按钮组款式.myButtonGroup > .myButton { border-radius: unset !important; // 给所有的按钮都去掉圆角 border-right: 1px solid #fff; // 给按钮加上分隔线条}// 第一个按钮左侧圆角.myButtonGroup > .myButton:first-of-type { border-top-left-radius: 5px !important; border-bottom-left-radius: 5px !important;}// 最初一个按钮的右侧圆角.myButtonGroup > .myButton:last-of-type { border-top-right-radius: 5px !important; border-bottom-right-radius: 5px !important; border-right: none; // 同时,革除最初一个按钮的右侧边框}
代码
复制粘贴即可应用,如果道友感觉代码帮忙到了您,欢送给咱github仓库一个star哈
myButton组件
<template> <button :style="styleCal" :class="[ 'myButton', disabled ? 'disabledBtn' : '', loading ? 'loadingBtn' : '', type, ]" :disabled="disabled || loading" @click="clickButton" > <i class="el-icon-loading iii" v-if="loading"></i> <!-- 应用传进来的图标,通过动静style管制图标和文字见的距离,同一时刻下, 只能有一个图标呈现,所以有loading图标了,就不能有别的图标了 --> <i :class="icon" :style="styleGap" v-if="icon && !loading"></i> <!-- 一般插槽有货色才去渲染 --> <span v-if="$slots.default"><slot></slot></span> </button></template><script>// 类型校验const typeArr = [ "", "primary", "success", "warning", "error", "text", "dangerText",];const sizeArr = ["", "small", "middle", "big"]; // 大小测验const sizeObj = { // 不同的大小指定不同的缩放水平 small: 0.85, middle: 1, big: 1.2,};export default { name: "myButton", props: { disabled: Boolean, loading: Boolean, // loading时,不可持续点击(持续点击不失效) rightIcon: Boolean, // 通过弹性盒的方向管制图标的地位 type: { type: String, validator(val) { return typeArr.includes(val); }, }, size: { type: String, validator(val) { return sizeArr.includes(val); }, }, icon: String, }, computed: { styleCal() { // 管制缩放和指定默认圆角以及设置图标在文字左侧还是右侧 let styleObj = { zoom: sizeObj[this.size], borderRadius: "5px", flexDirection: this.rightIcon ? "row-reverse" : "row", }; // 当有图标,且没有文字的时候(或默认插槽没传),就让按钮变成圆形按钮 if ((this.icon && !this.$slots.default) || !this.$slots.default[0].text) { styleObj["borderRadius"] = "50%"; styleObj["padding"] = "12px"; } return styleObj; }, styleGap() { // 有图标,有文字,图标在左侧 if ( (this.icon && !this.$slots.default) || (this.$slots.default[0].text && !this.rightIcon) ) { return { paddingRight: "1px", }; } // 有图标,有文字,图标在右侧 if ( (this.icon && !this.$slots.default) || (this.$slots.default[0].text && this.rightIcon) ) { return { paddingLeft: "1px", }; } }, }, methods: { clickButton(e) { if (this.disabled) return; this.$emit("click", e); // 传出去,便于应用 }, },};</script><style lang='less' scoped>/* 对于按钮的款式即写好几套款式,而后通过类型等各种参数去管制款式,最终实现对应成果 */// 根底款式.myButton { display: inline-flex; align-items: center; justify-content: center; white-space: nowrap; box-sizing: border-box; padding: 12px 16px; background-color: rgba(0, 0, 0, 0.1); color: #222; border: none; cursor: pointer; user-select: none; transition: all 0.3s; font-size: 14px; .iii { margin-right: 4px; }}.myButton:hover { background-color: rgba(0, 0, 0, 0.2);}.myButton:active { background-color: rgba(0, 0, 0, 0.3);}// primary款式.primary { background-color: #1867c0; color: #fff;}.primary:hover { background-color: #0854ac;}.primary:active { background-color: #033d7f;}// success款式.success { background-color: #19be6b; color: #fff;}.success:hover { background-color: #0ea459;}.success:active { background-color: #008140;}// warning款式.warning { background-color: #ffc163; color: #fff;}.warning:hover { background-color: #db952d;}.warning:active { background-color: #b97b1d;}// error款式.error { background-color: #ff5252; color: #fff;}.error:hover { background-color: #fd3030;}.error:active { background-color: #d50000;}// text款式.text { background-color: unset; color: #409eff; padding: 2px 4px;}.text:hover { background-color: unset; opacity: 0.9;}.text:active { background-color: unset; opacity: 1; color: #1a7ada;}// dangerText款式.dangerText { background-color: unset; color: #ff5252; padding: 2px 4px;}.dangerText:hover { background-color: unset; opacity: 0.9;}.dangerText:active { background-color: unset; opacity: 1; color: #d50000;}// 加载按钮款式.loadingBtn { opacity: 0.6; pointer-events: none; // 值为none就没有hover和active成果了}// disabled款式(留神款式的程序).disabledBtn { background-color: rgba(0, 0, 0, 0.12); color: #bbb;}.disabledBtn:hover { opacity: 1; cursor: not-allowed; background-color: rgba(0, 0, 0, 0.12);}.disabledBtn:active { color: #bbb; opacity: 1; background-color: rgba(0, 0, 0, 0.12);}// 附上按钮组款式.myButtonGroup > .myButton { border-radius: unset !important; border-right: 1px solid #fff;}.myButtonGroup > .myButton:first-of-type { border-top-left-radius: 5px !important; border-bottom-left-radius: 5px !important;}.myButtonGroup > .myButton:last-of-type { border-top-right-radius: 5px !important; border-bottom-right-radius: 5px !important; border-right: none;}</style>
myButtonGroup组件
<template> <div class="myButtonGroup"> <slot></slot> </div></template><script>export default { name: "myButtonGroup",};</script><style>.myButtonGroup { display: inline-flex !important; align-items: center;}</style>
应用的时候
<template> <div> <h5>单个按钮</h5> <br /> <button @click="clickLoad">加载切换</button> <div class="btnBox"> <span class="btn" v-for="(item, index) of btnArr"> <my-button style="margin-right: 16px" :key="index" :type="item.type" :size="item.size" :disabled="item.disabled" :loading="item.loading" :icon="item.icon" :rightIcon="item.rightIcon" @click=" (e) => { clickBtn(item, e); } " >{{ item.name }}</my-button > </span> </div> <br /> <h5>按钮组</h5> <br /> <my-button-group> <my-button type="success" icon="el-icon-arrow-left">上一页</my-button> <my-button type="success" icon="el-icon-arrow-right" :rightIcon="true" >下一页</my-button > </my-button-group> <br /> <br /> <my-button-group> <my-button type="primary" icon="el-icon-user"></my-button> <my-button type="primary" icon="el-icon-view"></my-button> <my-button type="primary" icon="el-icon-star-off"></my-button> <my-button type="primary" icon="el-icon-chat-dot-square"></my-button> <my-button type="primary" icon="el-icon-share"></my-button> </my-button-group> </div></template><script>export default { name: "myButtonName", data() { return { loadingF: false, btnArr: [ { type: "", name: "默认按钮", }, { type: "primary", name: "primary", }, { type: "success", name: "success", }, { type: "warning", name: "warning", }, { type: "error", name: "error", }, { type: "primary", name: "size=small", size: "small", }, { type: "primary", name: "size=middle", size: "middle", }, { type: "primary", name: "size=big", size: "big", }, { type: "success", // 不论type什么类型,只有禁用全副置灰 name: "disabled", disabled: true, }, { type: "primary", name: "期待加载", loading: false, }, { type: "success", name: "期待加载", loading: false, }, { type: "success", name: "icon", icon: "el-icon-star-on", }, { type: "success", name: "icon", icon: "el-icon-star-on", rightIcon: true, }, { type: "success", name: "", icon: "el-icon-edit", }, { type: "error", name: "", icon: "el-icon-delete", }, { type: "text", name: "纯text按钮", // loading: true, }, { type: "dangerText", name: "dangerText按钮", icon: "el-icon-delete-solid", }, { type: "text", name: "text禁用", disabled: true, }, ], }; }, methods: { clickLoad() { let lebel = this.btnArr[9].name; let newItem9 = { type: "primary", name: lebel == "期待加载" ? "加载中" : "期待加载", loading: lebel == "期待加载" ? true : false, }; this.$set(this.btnArr, 9, newItem9); let newItem10 = { type: "success", name: lebel == "期待加载" ? "加载中" : "期待加载", loading: lebel == "期待加载" ? true : false, }; this.$set(this.btnArr, 10, newItem10); }, // 留神这种写法,可接管多个参数 clickBtn(item, e) { console.log("clickBtn", item, e); }, },};</script><style>.btnBox { width: 100%; box-sizing: border-box; padding: 24px 0; display: flex; align-items: flex-end; flex-wrap: wrap;}.btn { margin-bottom: 24px;}</style>
A good memory is better than a bad pen. Write it down...