2020轮子燥起来-首篇

前言

😄你们过年好!如今已经进入21世纪20年代了,20后的出现对于92年还没女友的我刺激仍是蛮大的,虽然还差点步入中年,但咱们仍是须要展望将来,期待着一份美好的爱情。这也是个人新年愿望,但愿和我有一样境遇的小伙伴在新的一年都能找到本身理想的另外一半儿👄。css

最近这两年前端技术真的是变幻无穷,让人透不过气来,但总有那些牛X的大佬及时的把新技术总结分享出来供咱们这些小白白吸取,虽然仍是记不太住...😫前端

做为一名前端儿,对广泛在公司写业务是最基本不过的事情了,使用过的轮子无数,但多是岁数大了,记性不是特别好,对使用过的轮子再次用到仍是免不了各类google、度娘走一波,诶心累😥。好久之前我就很是羡慕这些轮子的制造者,在平常工做中若可以封装组件会更加使本身的技术获得提高,基础更加扎实,说白了,封装组件这个套路那绝对是百利而无一害😘。vue

这篇做为2020年的起始篇,让咱们从头来封装几个组件,了解封装组件的思路,愿广大前端儿都跨过组件封装这个门槛儿,也是为想进大厂体验高逼格工做氛围的童鞋们作好准备🤪。git

所需技术储备

万事开头难,好的开头才会顺利的走好每一步。因此,封装以前咱们须要掌握一些前置知识点:web

  • vue
  • sass
  • ElementUI
  • git

只需熟悉以上4点咱们即可以踏上封装组件的旅程ajax

ElementUI做为一款很是优秀的前端UI库,在平时工做中使用的也是较多的,并且,我以为个人审美再牛X,也想不出来比人家还要好看的样式体验,不管是样式仍是代码上,我都会向它靠拢。很明显,我就是研究人家的源码来学习-😥-只不过我会尽可能把思路捋出来供哪些没思路的小伙伴儿有些帮助,同时也对我本身组件封装相关知识的巩固。对于有不对的地方望大佬们轻喷,多提意见,毕竟都是圈儿里的,说话要搂着点😭vue-cli

大过年的,碎碎叨叨的也差很少了,接下来,进入主题吧--最经常使用的组件莫过于button了,就用它来做为新年第一道菜,看看怎么样把它一步步炒成美食的。typescript

前边操做我就略过了哈:数组

  • vue-cli起个项目,同时配置sass用来写css
  • 删掉全部多余的文件
  • 新建components目录用来存放封装的组件
  • 新建views目录用来展现及使用封装的组件
  • 新建theme目录存放组件样式及公共样式文件
  • router.js中咱们就正常配置路由便可,用来展现咱们写过的每一个组件

附加:prettier+eslint来规范代码的书写,香不香谁用谁知道😋sass

封装前的准备工做

  • components/button/button.vue 在此文件中来封装button
  • views/button/button-view.vue 此文件用来使用封装过的button展现在页面上
  • theme/common 中存放公共的样式,包括字体、公共样式变量、及icon样式
  • theme/components 中存放各个对应的组件样式
  • theme/index.scss 用来导入全部上面的样式文件,便与组件文件访问
  • router/index.js 中配置对应的页面路由
{
    path: "/button",
    name: "button",
    component: Button
 },
复制代码

button.vue中写如一个原生button按钮,并引入到button-view.vue文件中

components/button.vue

<template>
  <button>按钮</button>
</template>
<script>
export default {
  name: "ZButton"
};
</script>
<style lang="scss" scoped></style>

复制代码
views/button/button-view.vue

<template>
  <z-button></z-button>
</template>
<script>
import ZButton from "@/components/button/button";
export default {
  name: "ZButtonView",
  components: {
    ZButton
  }
};
</script>
复制代码

到此,页面上会出现写的这个原生按钮。先不急,先来简单看下elemetnui中的源码结构:

能够看到,这个目录结构和咱们的不同,来看几个重要的地方:

  • example文件夹存放的是一些示例代码
  • src文件夹中存放的是一些指令工具方法
  • packages文件夹中存放的是全部组件的封装源码
  • packages/theme-chalk/src中存放的是全部样式相关文件
  • types文件夹存放的是对于typescript的声明文件

了解其文件结构便可,想看源码的同窗能够自行去深刻琢磨。这里并非要作成多么大的一个ui组件库,目的是实现一些组件的封装,了解其套路。

并且,ui库之因此为ui库,就是由于它们的样式很漂亮,咱们在使用时并不须要写过多的样式去重写,大部分状况下直接使用便可。这就证实了ui库中的样式文件代码是很重要的,并且有必定的规范。 在封装组件以前咱们先来梳理下样式相关的问题:

  • theme/common/vars.scss中先定义公共的样式变量
  • theme/common/font.scss中定义字体相关样式
  • theme/common/icon.scss中定义图标相关样式

由于样式代码太多了,这里就不贴出来了。我所用的都是elementui中的样式,只不过本身改动了些,没有人家那样规范。毕竟水平有限,css也是一门很大的学问,想要玩的精我感受仍是要下一番苦功夫的。

分析button组件

首先,咱们先来看看人家封装的button组件具备哪些特性:

  • 根据传入的type值显示不一样样式的按钮:defaultprimaryinfowarningsuccessdanger
  • 根据传入的size值显示不一样尺寸大小的按钮
  • 根据传入不一样的属性展现对应形状及状态的按钮:plainroundcircledisableloading
  • 根据传入native-type属性支持原生功能的按钮:buttonresetsubmit
  • 给按钮组件添加click事件,并触发父级组件的click事件,执行对应的业务逻辑
  • ButtonGroup按钮组

下面,咱们开始逐一实现elementUIbutton的相关功能。

根据type显示不一样样式的按钮

每套ui库使用时都会有一套本身的前缀,例如elementui中的el,我这参照人家来作,前缀定义为z

思路:根据传入不一样的type来显示不一样的样式的按钮,因此这里要把这个样式作成动态的,在vue中作成动态的可使用对象、数组的方式,而且这些值须要传递进来,涉及到父子组件传值的一些操做。

实现:

components/button.vue

<template>
  <button
  class="z-button"
  :class="[ // 动态绑定class `z-button-- + ${type}`, // 重点在这里,type是父组件传递过来的 ]"
  >
    <span>  // 该slot插槽用来显示传入的文本内容,以及后面要实现的右侧字体图标
      <slot></slot>
    </span>
  </button>
</template>
<script>
export default {
  name: "ZButton",
  props: {
    type: {  // 这里接受父组件传递过来的type属性,用来拼接动态class,生成不一样样式的按钮
      type: String,
      default: "default"
    }
  }
};
</script>
<style lang="scss" scoped>
@import "../../theme/components/button.scss"; // 组件样式在这里写的,因为代码过多,就不展现了
</style>

复制代码

其中还包括按钮的hoveractive等效果的样式代码

views/button/button-view.vue

<template>
  <div class="z-button-container">
    <div class="z-button-wrap">
      <div class="z-row">
        <z-button>不一样样式的按钮</z-button>
        <z-button type="primary">主要按钮</z-button>
        <z-button type="success">成功按钮</z-button>
        <z-button type="info">信息按钮</z-button>
        <z-button type="warning">警告按钮</z-button>
        <z-button type="danger">危险按钮</z-button>
      </div>
    </div>
 </div>
</template>
<script>
import ZButton from "@/components/button/button";
export default {
  name: "ZButtonView",
  components: {
    ZButton
  }
};
</script>
 <style lang="scss" scoped>
 // 这里的css代码只是为了布局展现美观一些,跟封装组件的样式没啥关系
.z-button-wrap .z-row {
  margin-bottom: 20px;
}
.z-button-wrap .z-button + .z-button {
  margin-left: 10px;
}
.z-button-wrap .z-button-group + .z-button-group {
  margin-left: 10px;
}
.z-button-wrap .z-button-group .z-button {
  margin-left: 0;
}
.z-button-wrap .z-button-group {
  margin-bottom: 10px;
}
</style>

复制代码

此时,在页面上会看到根据传入的不一样的type,会展现不一样样式的按钮了

根据传入的size值显示不一样尺寸大小的按钮

思路:其实和上面定义不一样样式的路子是同样的,只不过此次是根据传入的size改变尺寸而已。咱们能够把传入的size做为计算属性来实现绑定不一样的样式,同时能够要求开发人员在使用的时候只能传入mediumsmallmini这几个值,若是传入别的值进行报错用来提示开发人员 实现:

components/button.vue

<template>
  <button
  class="z-button"
  :class="[ // 动态绑定class `z-button-- + ${type}`, // 重点在这里,type是父组件传递过来的 buttonSizeClass // 使用计算属性 ]"
  >
    <span>  // 该slot插槽用来显示传入的文本内容,以及后面要实现的右侧字体图标
      <slot></slot>
    </span>
  </button>
</template>
<script>
export default {
  name: "ZButton",
  props: {
    type: {  // 这里接受父组件传递过来的type属性,用来拼接动态class,生成不一样样式的按钮
      type: String,
      default: "default"
    },
    size: { // 父组件传递过来的值,经过validator校验器来规定传入的值的范围
      type: String,
      validator: value => {
        return ["medium", "small", "mini"].indexOf(value) !== -1;
      }
    }
  },
  computed: {  
    buttonSizeClass() { // 该计算属性用来计算class样式
      if (this.size) {
        return "z-button--" + this.size;
      }
      return null;
    }
  },
};
</script>
<style lang="scss" scoped>
@import "../../theme/components/button.scss"; // 组件样式在这里写的,因为代码过多,就不展现了,按钮尺寸相关样式也已添加在此文件中
</style>
复制代码
views/button/button-view.vue

<template>
  <div class="z-button-container">
    <div class="z-button-wrap">
     <div class="z-row">
        <z-button>不一样样式的按钮</z-button>
        <z-button type="primary">主要按钮</z-button>
        <z-button type="success">成功按钮</z-button>
        <z-button type="info">信息按钮</z-button>
        <z-button type="warning">警告按钮</z-button>
        <z-button type="danger">危险按钮</z-button>
      </div>
      <div class="z-row">
        <z-button>不一样尺寸的按钮</z-button>
        <z-button type="primary" size="medium">主要按钮</z-button>
        <z-button type="success" size="small">成功按钮</z-button>
        <z-button type="info" size="mini">信息按钮</z-button>
      </div>
    </div>
 </div>
</template>
<script>
import ZButton from "@/components/button/button";
export default {
  name: "ZButtonView",
  components: {
    ZButton
  }
};
</script>
 <style lang="scss" scoped>
 // 这里的css代码只是为了布局展现美观一些,跟封装组件的样式没啥关系
.z-button-wrap .z-row {
  margin-bottom: 20px;
}
.z-button-wrap .z-button + .z-button {
  margin-left: 10px;
}
.z-button-wrap .z-button-group + .z-button-group {
  margin-left: 10px;
}
.z-button-wrap .z-button-group .z-button {
  margin-left: 0;
}
.z-button-wrap .z-button-group {
  margin-bottom: 10px;
}
</style>

复制代码

按钮形状

思路:elementUI中分为朴素、圆角、圆形按钮,能够根据父组件传入的布尔值来判断该按钮组件加入不一样的样式类来实现。 实现:

components/button.vue

<template>
  <button
    class="z-button"
    :class="[ // 动态绑定class `z-button-- + ${type}`, // 重点在这里,type是父组件传递过来的 buttonSizeClass, // 使用计算属性 { // 以对象的形式为按钮添加不一样形状的样式类 'is-plain': plain, 'is-round': round, 'is-circle': circle } ]"
  >
    <span>  // 该slot插槽用来显示传入的文本内容,以及后面要实现的右侧字体图标
      <slot></slot>
    </span>
  </button>
</template>
<script>
export default {
  name: "ZButton",
  props: {
    type: {  // 这里接受父组件传递过来的type属性,用来拼接动态class,生成不一样样式的按钮
      type: String,
      default: "default"
    },
    size: { // 父组件传递过来的值,经过validator校验器来规定传入的值的范围
      type: String,
      validator: value => {
        return ["medium", "small", "mini"].indexOf(value) !== -1;
      }
    },
    // 如下三个布尔值来做为样式的标志状态,默认为false,为true则添加对应的样式类
    // 样式代码已在button.scss中写好
    plain: {
      type: Boolean,
      default: false
    },
    round: {
      type: Boolean,
      default: false
    },
    circle: {
      type: Boolean,
      default: false
    }
  },
  computed: {  
    buttonSizeClass() { // 该计算属性用来计算class样式
      if (this.size) {
        return "z-button--" + this.size;
      }
      return null;
    }
  },
};
</script>
<style lang="scss" scoped>
@import "../../theme/components/button.scss"; // 组件样式在这里写的,因为代码过多,就不展现了,按钮尺寸相关样式也已添加在此文件中
</style>
复制代码
views/button/button-view.vue

<template>
  <div class="z-button-container">
    <div class="z-button-wrap">
      <div class="z-row">
        <z-button>不一样样式的按钮</z-button>
        <z-button type="primary">主要按钮</z-button>
        <z-button type="success">成功按钮</z-button>
        <z-button type="info">信息按钮</z-button>
        <z-button type="warning">警告按钮</z-button>
        <z-button type="danger">危险按钮</z-button>
      </div>
      <div class="z-row">
        <z-button>不一样尺寸的按钮</z-button>
        <z-button type="primary" size="medium">主要按钮</z-button>
        <z-button type="success" size="small">成功按钮</z-button>
        <z-button type="info" size="mini">信息按钮</z-button>
      </div>
      <div class="z-row">
        <z-button>不一样形状的按钮</z-button>
        <z-button type="primary" plain>主要按钮</z-button>
        <z-button type="success" circle>成功按钮</z-button>
        <z-button type="info" round>信息按钮</z-button>
      </div>
    </div>
 </div>
</template>
<script>
import ZButton from "@/components/button/button";
export default {
  name: "ZButtonView",
  components: {
    ZButton
  }
};
</script>
 <style lang="scss" scoped>
 // 这里的css代码只是为了布局展现美观一些,跟封装组件的样式没啥关系
.z-button-wrap .z-row {
  margin-bottom: 20px;
}
.z-button-wrap .z-button + .z-button {
  margin-left: 10px;
}
.z-button-wrap .z-button-group + .z-button-group {
  margin-left: 10px;
}
.z-button-wrap .z-button-group .z-button {
  margin-left: 0;
}
.z-button-wrap .z-button-group {
  margin-bottom: 10px;
}
</style>
复制代码

添加icon支持

思路:首先把elementui中的字体文件拷贝到我们本身的src/assets目录中,在theme/common/font.scss中引入字体文件进行使用:

theme/common/font.scss

@font-face {
  font-family: "element-icons";
  src: url(../assets/element-icons.woff) format("woff"),
    url(../assets/element-icons.ttf) format("truetype");
  font-weight: normal;
  font-style: normal;
}
[class*=" z-icon-"],
[class^="z-icon-"] {
  font-family: element-icons !important;
  speak: none;
  font-style: normal;
  font-weight: 400;
  font-variant: normal;
  text-transform: none;
  line-height: 1;
  vertical-align: baseline;
  display: inline-block;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}
复制代码

theme/common/icon.scss中写入全部icon相关字体,及样式,这里我就简单写下,只包含了editloadingicon图标,其实能够把全部的图标都写在这里面:

theme/common/icon.scss

.z-icon-edit:before { 
  content: "\e78c";
}
.z-icon-loading:before {
  content: "\e6cf";
}
@keyframes rotating { // loading效果旋转动画
  0% {
    transform: rotateZ(0deg);
  }
  100% {
    transform: rotateZ(360deg);
  }
}
.z-icon-loading { // loading效果旋转动画
  animation: rotating 2s linear infinite;
}
复制代码

接下来,实现icon的支持

components/button.vue

<template>
  <button
    class="z-button"
    :class="[ // 动态绑定class `z-button-- + ${type}`, // 重点在这里,type是父组件传递过来的 buttonSizeClass, // 使用计算属性 { // 以对象的形式为按钮添加不一样形状的样式类 'is-plain': plain, 'is-round': round, 'is-circle': circle } ]"
  >
    <i v-if="icon" :class="icon"></i> // icon显示的位置
    <span v-if="$slots.default"> // 这里作个判断,只有插槽内有内容才展现,由于有图标按钮,没文字的那种
      <slot></slot> // 该slot插槽用来显示传入的文本内容,以及后面要实现的右侧字体图标
    </span>
  </button>
</template>
<script>
export default {
  name: "ZButton",
  props: {
    type: {  // 这里接受父组件传递过来的type属性,用来拼接动态class,生成不一样样式的按钮
      type: String,
      default: "default"
    },
    size: { // 父组件传递过来的值,经过validator校验器来规定传入的值的范围
      type: String,
      validator: value => {
        return ["medium", "small", "mini"].indexOf(value) !== -1;
      }
    },
    // 如下三个布尔值来做为样式的标志状态,默认为false,为true则添加对应的样式类
    // 样式代码已在button.scss中写好
    plain: {
      type: Boolean,
      default: false
    },
    round: {
      type: Boolean,
      default: false
    },
    circle: {
      type: Boolean,
      default: false
    },
    icon: String // 接受父组件传递过来的icon值
  },
  computed: {  
    buttonSizeClass() { // 该计算属性用来计算class样式
      if (this.size) {
        return "z-button--" + this.size;
      }
      return null;
    }
  },
};
</script>
<style lang="scss" scoped>
@import "../../theme/components/button.scss"; // 组件样式在这里写的,因为代码过多,就不展现了,按钮尺寸相关样式也已添加在此文件中
</style>
复制代码
views/button/button-view.vue

<template>
  <div class="z-button-container">
    <div class="z-button-wrap">
      <div class="z-row">
        <z-button>不一样样式的按钮</z-button>
        <z-button type="primary">主要按钮</z-button>
        <z-button type="success">成功按钮</z-button>
        <z-button type="info">信息按钮</z-button>
        <z-button type="warning">警告按钮</z-button>
        <z-button type="danger">危险按钮</z-button>
      </div>
      <div class="z-row">
        <z-button>不一样尺寸的按钮</z-button>
        <z-button type="primary" size="medium">主要按钮</z-button>
        <z-button type="success" size="small">成功按钮</z-button>
        <z-button type="info" size="mini">信息按钮</z-button>
      </div>
      <div class="z-row">
        <z-button>不一样形状的按钮</z-button>
        <z-button type="primary" plain>主要按钮</z-button>
        <z-button type="success" circle>成功按钮</z-button>
        <z-button type="info" round>信息按钮</z-button>
      </div>
      <div class="z-row">
        <z-button plain>默认按钮</z-button>
        <z-button type="primary" :loading="loading" plain icon="z-icon-edit">
          主要按钮
          <i class="z-icon-edit z-icon--right"></i> // 子组件的插槽存放右侧字体图标做用体如今这里
        </z-button>
      </div>
    </div>
 </div>
</template>
<script>
import ZButton from "@/components/button/button";
export default {
  name: "ZButtonView",
  components: {
    ZButton
  }
};
</script>
 <style lang="scss" scoped>
 // 这里的css代码只是为了布局展现美观一些,跟封装组件的样式没啥关系
.z-button-wrap .z-row {
  margin-bottom: 20px;
}
.z-button-wrap .z-button + .z-button {
  margin-left: 10px;
}
.z-button-wrap .z-button-group + .z-button-group {
  margin-left: 10px;
}
.z-button-wrap .z-button-group .z-button {
  margin-left: 0;
}
.z-button-wrap .z-button-group {
  margin-bottom: 10px;
}
</style>
复制代码

loadingdisabled

思路:定义好loading状态的样式,根据传入的布尔值来决定是不是loadingdisabled状态。而且由于loading的时候也是不可点击的,即禁用状态,样式不一样,但功能相似。因此咱们能够把这两个属性写在一块儿。还有一点就是字体图标按钮若是显示loading状态,那么该字体图标就不要显示了。 实现:

components/button.vue

<template>
  <button
  :disabled="disable || loading"  // 用同一个属性实现禁用状态便可
  class="z-button"
  :class="[ // 动态绑定class `z-button-- + ${type}`, // 重点在这里,type是父组件传递过来的 buttonSizeClass, // 使用计算属性 { // 以对象的形式为按钮添加不一样形状的样式类 'is-plain': plain, 'is-round': round, 'is-circle': circle } ]"
  >
    <i v-if="!loading && icon" :class="icon"></i> // 这里加入loading判断条件,loading为false图标才显示
    <i v-if="loading" class="z-icon-loading"></i> // loading图标,css其实就是让图标进行循环旋转
    <span v-if="$slots.default"> // 这里作个判断,只有插槽内有内容才展现,由于有图标按钮,没文字的那种
      <slot></slot> // 该slot插槽用来显示传入的文本内容,以及后面要实现的右侧字体图标
    </span>
  </button>
</template>
<script>
export default {
  name: "ZButton",
  props: {
    type: {  // 这里接受父组件传递过来的type属性,用来拼接动态class,生成不一样样式的按钮
      type: String,
      default: "default"
    },
    size: { // 父组件传递过来的值,经过validator校验器来规定传入的值的范围
      type: String,
      validator: value => {
        return ["medium", "small", "mini"].indexOf(value) !== -1;
      }
    },
    // 如下三个布尔值来做为样式的标志状态,默认为false,为true则添加对应的样式类
    // 样式代码已在button.scss中写好
    plain: {
      type: Boolean,
      default: false
    },
    round: {
      type: Boolean,
      default: false
    },
    circle: {
      type: Boolean,
      default: false
    },
    disable: {
      type: Boolean,
      default: false
    },
    loading: Boolean
  },
  computed: {  
    buttonSizeClass() { // 该计算属性用来计算class样式
      if (this.size) {
        return "z-button--" + this.size;
      }
      return null;
    }
  },
};
</script>
<style lang="scss" scoped>
@import "../../theme/components/button.scss"; // 组件样式在这里写的,因为代码过多,就不展现了,按钮尺寸相关样式也已添加在此文件中
</style>
复制代码
views/button/button-view.vue

<template>
  <div class="z-button-container">
    <div class="z-button-wrap">
      <div class="z-row">
        <z-button>不一样样式的按钮</z-button>
        <z-button type="primary">主要按钮</z-button>
        <z-button type="success">成功按钮</z-button>
        <z-button type="info">信息按钮</z-button>
        <z-button type="warning">警告按钮</z-button>
        <z-button type="danger">危险按钮</z-button>
      </div>
      <div class="z-row">
        <z-button>不一样尺寸的按钮</z-button>
        <z-button type="primary" size="medium">主要按钮</z-button>
        <z-button type="success" size="small">成功按钮</z-button>
        <z-button type="info" size="mini">信息按钮</z-button>
      </div>
      <div class="z-row">
        <z-button>不一样形状的按钮</z-button>
        <z-button type="primary" plain>主要按钮</z-button>
        <z-button type="success" circle>成功按钮</z-button>
        <z-button type="info" round>信息按钮</z-button>
      </div>
       <div class="z-row">
        <z-button>禁用及loading按钮</z-button>
        <z-button type="primary" plain disabled>主要按钮</z-button>
        <z-button type="success" circle loading>成功按钮</z-button>
      </div>
    </div>
 </div>
</template>
<script>
import ZButton from "@/components/button/button";
export default {
  name: "ZButtonView",
  components: {
    ZButton
  }
};
</script>
 <style lang="scss" scoped>
 // 这里的css代码只是为了布局展现美观一些,跟封装组件的样式没啥关系
.z-button-wrap .z-row {
  margin-bottom: 20px;
}
.z-button-wrap .z-button + .z-button {
  margin-left: 10px;
}
.z-button-wrap .z-button-group + .z-button-group {
  margin-left: 10px;
}
.z-button-wrap .z-button-group .z-button {
  margin-left: 0;
}
.z-button-wrap .z-button-group {
  margin-bottom: 10px;
}
</style>
复制代码

给按钮添加click事件

思路:给按钮绑定click事件,都是用来处理对应的业务逻辑,因此要把事件往外传递,在父组件中去处理业务逻辑。 实现:

components/button.vue

<template>
  <button
  :disabled="disable || loading"  // 用同一个属性实现禁用状态便可
  class="z-button"
  :class="[ // 动态绑定class `z-button-- + ${type}`, // 重点在这里,type是父组件传递过来的 buttonSizeClass, // 使用计算属性 { // 以对象的形式为按钮添加不一样形状的样式类 'is-plain': plain, 'is-round': round, 'is-circle': circle } ]"
    @click="handleClick" // 给按钮绑定click事件
  >
    <i v-if="!loading && icon" :class="icon"></i> // 这里加入loading判断条件,loading为false图标才显示
    <i v-if="loading" class="z-icon-loading"></i> // loading图标,css其实就是让图标进行循环旋转
    <span v-if="$slots.default"> // 这里作个判断,只有插槽内有内容才展现,由于有图标按钮,没文字的那种
      <slot></slot> // 该slot插槽用来显示传入的文本内容,以及后面要实现的右侧字体图标
    </span>
  </button>
</template>
<script>
export default {
  name: "ZButton",
  props: {
    type: {  // 这里接受父组件传递过来的type属性,用来拼接动态class,生成不一样样式的按钮
      type: String,
      default: "default"
    },
    size: { // 父组件传递过来的值,经过validator校验器来规定传入的值的范围
      type: String,
      validator: value => {
        return ["medium", "small", "mini"].indexOf(value) !== -1;
      }
    },
    // 如下三个布尔值来做为样式的标志状态,默认为false,为true则添加对应的样式类
    // 样式代码已在button.scss中写好
    plain: {
      type: Boolean,
      default: false
    },
    round: {
      type: Boolean,
      default: false
    },
    circle: {
      type: Boolean,
      default: false
    },
    disable: {
      type: Boolean,
      default: false
    },
    loading: Boolean
  },
  computed: {  
    buttonSizeClass() { // 该计算属性用来计算class样式
      if (this.size) {
        return "z-button--" + this.size;
      }
      return null;
    }
  },
  methods: {
    handleClick(event) { // 触发父级click事件,去处理对应的业务逻辑
      this.$emit("click", event);
    }
  }
};
</script>
<style lang="scss" scoped>
@import "../../theme/components/button.scss"; // 组件样式在这里写的,因为代码过多,就不展现了,按钮尺寸相关样式也已添加在此文件中
</style>
复制代码
views/button/button-view.vue

<template>
  <div class="z-button-container">
    <div class="z-button-wrap">
      <div class="z-row">
        <z-button>不一样样式的按钮</z-button>
        <z-button type="primary">主要按钮</z-button>
        <z-button type="success">成功按钮</z-button>
        <z-button type="info">信息按钮</z-button>
        <z-button type="warning">警告按钮</z-button>
        <z-button type="danger">危险按钮</z-button>
      </div>
      <div class="z-row">
        <z-button>不一样尺寸的按钮</z-button>
        <z-button type="primary" size="medium">主要按钮</z-button>
        <z-button type="success" size="small">成功按钮</z-button>
        <z-button type="info" size="mini">信息按钮</z-button>
      </div>
      <div class="z-row">
        <z-button>不一样形状的按钮</z-button>
        <z-button type="primary" plain>主要按钮</z-button>
        <z-button type="success" circle>成功按钮</z-button>
        <z-button type="info" round>信息按钮</z-button>
      </div>
       <div class="z-row">
        <z-button>禁用及loading按钮</z-button>
        <z-button type="primary" plain disabled>主要按钮</z-button>
        <z-button type="success" circle loading>成功按钮</z-button>
      </div>
      <div class="z-row">
        <z-button @click="handleParentClick">带有click事件的按钮</z-button>
        <z-button :loading="loading">loading按钮</z-button>
      </div>
    </div>
 </div>
</template>
<script>
import ZButton from "@/components/button/button";
export default {
  name: "ZButtonView",
  data() {
    return {
      loading: false
    };
  },
  components: {
    ZButton
  },
  methods: {
    handleParentClick() {
      // 执行业务逻辑。好比发送ajax请求,开启Loading,请求结束关闭loading 
      this.loading = true
      // 发送请求
      setTimeout(()=>{
        this.loading = false  
      },2000)
    } // 此时点击按钮会触发loading效果,并在2s后关闭loading
  }
};
</script>
 <style lang="scss" scoped>
 // 这里的css代码只是为了布局展现美观一些,跟封装组件的样式没啥关系
.z-button-wrap .z-row {
  margin-bottom: 20px;
}
.z-button-wrap .z-button + .z-button {
  margin-left: 10px;
}
.z-button-wrap .z-button-group + .z-button-group {
  margin-left: 10px;
}
.z-button-wrap .z-button-group .z-button {
  margin-left: 0;
}
.z-button-wrap .z-button-group {
  margin-bottom: 10px;
}
</style>
复制代码

让按钮组件支持原生方法

思路:动态绑定按钮的type属性,父组件传值,实现原生方法的功能。 实现:

components/button.vue

<template>
  <button
  :type="nativeType" // 动态绑定type属性
  :disabled="disable || loading"  // 用同一个属性实现禁用状态便可
  class="z-button"
  :class="[ // 动态绑定class `z-button-- + ${type}`, // 重点在这里,type是父组件传递过来的 buttonSizeClass, // 使用计算属性 { // 以对象的形式为按钮添加不一样形状的样式类 'is-plain': plain, 'is-round': round, 'is-circle': circle } ]"
    @click="handleClick" // 给按钮绑定click事件
  >
    <i v-if="!loading && icon" :class="icon"></i> // 这里加入loading判断条件,loading为false图标才显示
    <i v-if="loading" class="z-icon-loading"></i> // loading图标,css其实就是让图标进行循环旋转
    <span v-if="$slots.default"> // 这里作个判断,只有插槽内有内容才展现,由于有图标按钮,没文字的那种 
      <slot></slot> // 该slot插槽用来显示传入的文本内容,以及后面要实现的右侧字体图标
    </span>
  </button>
</template>
<script>
export default {
  name: "ZButton",
  props: {
    type: {  // 这里接受父组件传递过来的type属性,用来拼接动态class,生成不一样样式的按钮
      type: String,
      default: "default"
    },
    size: { // 父组件传递过来的值,经过validator校验器来规定传入的值的范围
      type: String,
      validator: value => {
        return ["medium", "small", "mini"].indexOf(value) !== -1;
      }
    },
    // 如下三个布尔值来做为样式的标志状态,默认为false,为true则添加对应的样式类
    // 样式代码已在button.scss中写好
    plain: {
      type: Boolean,
      default: false
    },
    round: {
      type: Boolean,
      default: false
    },
    circle: {
      type: Boolean,
      default: false
    },
    disable: {
      type: Boolean,
      default: false
    },
    loading: Boolean,
    nativeType: { // 获取父组件传递过来的type值,并用validator校验让使用者只能选择一下三种之一
      type: String,
      default: "button",
      validator: value => {
        return ["button", "reset", "submit"].indexOf(value) !== -1;
      }
    },
  },
  computed: {  
    buttonSizeClass() { // 该计算属性用来计算class样式
      if (this.size) {
        return "z-button--" + this.size;
      }
      return null;
    }
  },
  methods: {
    handleClick(event) { // 触发父级click事件,去处理对应的业务逻辑
      this.$emit("click", event);
    }
  }
};
</script>
<style lang="scss" scoped>
@import "../../theme/components/button.scss"; // 组件样式在这里写的,因为代码过多,就不展现了,按钮尺寸相关样式也已添加在此文件中
</style>
复制代码
views/button/button-view.vue

<template>
  <div class="z-button-container">
    <div class="z-button-wrap">
      <div class="z-row">
        <z-button>不一样样式的按钮</z-button>
        <z-button type="primary">主要按钮</z-button>
        <z-button type="success">成功按钮</z-button>
        <z-button type="info">信息按钮</z-button>
        <z-button type="warning">警告按钮</z-button>
        <z-button type="danger">危险按钮</z-button>
      </div>
      <div class="z-row">
        <z-button>不一样尺寸的按钮</z-button>
        <z-button type="primary" size="medium">主要按钮</z-button>
        <z-button type="success" size="small">成功按钮</z-button>
        <z-button type="info" size="mini">信息按钮</z-button>
      </div>
      <div class="z-row">
        <z-button>不一样形状的按钮</z-button>
        <z-button type="primary" plain>主要按钮</z-button>
        <z-button type="success" circle>成功按钮</z-button>
        <z-button type="info" round>信息按钮</z-button>
      </div>
       <div class="z-row">
        <z-button>禁用及loading按钮</z-button>
        <z-button type="primary" plain disabled>主要按钮</z-button>
        <z-button type="success" circle loading>成功按钮</z-button>
      </div>
      <div class="z-row">
        <z-button @click="handleParentClick">带有click事件的按钮</z-button>
        <z-button :loading="loading">loading按钮</z-button>
      </div>
      <div class="z-row">
        <z-button autofocus>支持原生事件按钮</z-button>
        <z-button native-type="submit">提交按钮</z-button>
        <z-button native-type="reset" type="primary">重置按钮</z-button>
      </div>
    </div>
 </div>
</template>
<script>
import ZButton from "@/components/button/button";
export default {
  name: "ZButtonView",
  data() {
    return {
      loading: false
    };
  },
  components: {
    ZButton
  },
  methods: {
    handleParentClick() {
      // 执行业务逻辑。好比发送ajax请求,开启Loading,请求结束关闭loading 
      this.loading = true
      // 发送请求
      setTimeout(()=>{
        this.loading = false  
      },2000)
    } // 此时点击按钮会触发loading效果,并在2s后关闭loading
  }
};
</script>
 <style lang="scss" scoped>
 // 这里的css代码只是为了布局展现美观一些,跟封装组件的样式没啥关系
.z-button-wrap .z-row {
  margin-bottom: 20px;
}
.z-button-wrap .z-button + .z-button {
  margin-left: 10px;
}
.z-button-wrap .z-button-group + .z-button-group {
  margin-left: 10px;
}
.z-button-wrap .z-button-group .z-button {
  margin-left: 0;
}
.z-button-wrap .z-button-group {
  margin-bottom: 10px;
}
</style>
复制代码

实现按钮组

思路:按钮组的功能实际上就是在外层作曾包裹,把按钮组件放入其中,难点在于css的样式控制,两端的按钮有圆角,中间若是有多个按钮都没有圆角,用css的选择器想好逻辑作对应的处理便可。 实现: 这里新建一个文件: /components/button-group.vue

/components/button-group.vue
 
<template>
  <div class="z-button-group">
    <slot></slot>
  </div>
</template>
<script>
export default {
  name: "ZButtonGroup"
};
</script>
<style lang="scss" scoped>
@import "../../theme/components/button-group.scss"; 
</style>

复制代码

button-group的样式并不算多,我先展现在这里,这里须要和button的样式结合。

.z-button-group {
  display: inline-block;
  .z-button + .z-button {
    margin-left: 0;
  }
  .z-button:first-child:not(:last-child) {
    border-top-right-radius: 0;
    border-bottom-right-radius: 0;
  }
  .z-button:last-child:not(:first-child) {
    border-top-left-radius: 0;
    border-bottom-left-radius: 0;
  }
  .z-button:not(:first-child):not(:last-child) {
    border-top-left-radius: 0;
    border-top-right-radius: 0;
    border-bottom-left-radius: 0;
    border-bottom-right-radius: 0;
  }
  .z-button--primary:last-child:not(:first-child) {
    border-left-color: rgba(255, 255, 255, 0.5);
  }
  .z-button--primary:not(:last-child):not(:first-child) {
    border-left-color: rgba(255, 255, 255, 0.5);
  }
  .z-button--success:last-child:not(:first-child) {
    border-left-color: rgba(255, 255, 255, 0.5);
  }
  .z-button--success:not(:last-child):not(:first-child) {
    border-left-color: rgba(255, 255, 255, 0.5);
  }
  .z-button--info:last-child:not(:first-child) {
    border-left-color: rgba(255, 255, 255, 0.5);
  }
  .z-button--info:not(:last-child):not(:first-child) {
    border-left-color: rgba(255, 255, 255, 0.5);
  }
  .z-button--warning:last-child:not(:first-child) {
    border-left-color: rgba(255, 255, 255, 0.5);
  }
  .z-button--warning:not(:last-child):not(:first-child) {
    border-left-color: rgba(255, 255, 255, 0.5);
  }
  .z-button--danger:last-child:not(:first-child) {
    border-left-color: rgba(255, 255, 255, 0.5);
  }
  .z-button--danger:not(:last-child):not(:first-child) {
    border-left-color: rgba(255, 255, 255, 0.5);
  }
}
复制代码
views/button/button-view.vue
<template>
  <div class="z-button-container">
    <div class="z-button-wrap">
      <div class="z-row">
        <z-button>不一样样式的按钮</z-button>
        <z-button type="primary">主要按钮</z-button>
        <z-button type="success">成功按钮</z-button>
        <z-button type="info">信息按钮</z-button>
        <z-button type="warning">警告按钮</z-button>
        <z-button type="danger">危险按钮</z-button>
      </div>
      <div class="z-row">
        <z-button>不一样尺寸的按钮</z-button>
        <z-button type="primary" size="medium">主要按钮</z-button>
        <z-button type="success" size="small">成功按钮</z-button>
        <z-button type="info" size="mini">信息按钮</z-button>
      </div>
      <div class="z-row">
        <z-button>不一样形状的按钮</z-button>
        <z-button type="primary" plain>主要按钮</z-button>
        <z-button type="success" circle>成功按钮</z-button>
        <z-button type="info" round>信息按钮</z-button>
      </div>
       <div class="z-row">
        <z-button>禁用及loading按钮</z-button>
        <z-button type="primary" plain disabled>主要按钮</z-button>
        <z-button type="success" circle loading>成功按钮</z-button>
      </div>
      <div class="z-row">
        <z-button @click="handleParentClick">带有click事件的按钮</z-button>
        <z-button :loading="loading">loading按钮</z-button>
      </div>
      <div class="z-row">
        <z-button autofocus>支持原生事件按钮</z-button>
        <z-button native-type="submit">提交按钮</z-button>
        <z-button native-type="reset" type="primary">重置按钮</z-button>
      </div>
      <div class="z-row">
        <z-button-group>
          <z-button type="warning" plain>按钮组</z-button>
          <z-button type="warning" plain>上一页</z-button>
          <z-button type="warning" plain>下一页</z-button>
          <z-button type="warning" plain>下一页</z-button>
        </z-button-group>
        <z-button-group>
          <z-button type="success" circle></z-button>
          <z-button type="success" circle></z-button>
          <z-button type="success" circle></z-button>
        </z-button-group>
      </div>
    </div>
 </div>
</template>
<script>
import ZButton from "@/components/button/button";
import ZButtonGroup from "@/components/button-group/button-group"; // 引入buttongroup
export default {
  name: "ZButtonView",
  data() {
    return {
      loading: false
    };
  },
  components: {
    ZButton,
    ZButtonGroup
  },
  methods: {
    handleParentClick() {
      // 执行业务逻辑。好比发送ajax请求,开启Loading,请求结束关闭loading 
      this.loading = true
      // 发送请求
      setTimeout(()=>{
        this.loading = false  
      },2000)
    } // 此时点击按钮会触发loading效果,并在2s后关闭loading
  }
};
</script>
 <style lang="scss" scoped>
 // 这里的css代码只是为了布局展现美观一些,跟封装组件的样式没啥关系
.z-button-wrap .z-row {
  margin-bottom: 20px;
}
.z-button-wrap .z-button + .z-button {
  margin-left: 10px;
}
.z-button-wrap .z-button-group + .z-button-group {
  margin-left: 10px;
}
.z-button-wrap .z-button-group .z-button {
  margin-left: 0;
}
.z-button-wrap .z-button-group {
  margin-bottom: 10px;
}
</style>
复制代码

经过以上的代码,button组件的封装差很少都封装完毕了,后续整理一下再把这部分总体的代码贴出来,代码中主要的地方我加上了注释,但愿对某些童鞋们能有个总体思路的了解。

今天也是年前在公司的最后一天了,这一年过得也是晕晕乎乎,好的一点是年会得了个二等奖,虽然不是啥贵重东西,起码也预示着新的一年会有个好运吧😜。接下来就是回家过年了😱,没女票都不知道该咋汇报,诶。年后有时间再继续搞一波别的组件,这东西挺费神的,归根结底自身能力仍是有很大欠缺,还需继续努力。

2020新的愿望: 争取找到另外一半儿,技术方面再升点段位🙃,也就这点追求了。

相关文章
相关标签/搜索