本篇文章记录仿写一个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...