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新的願望: 爭取找到另外一半兒,技術方面再升點段位🙃,也就這點追求了。

相關文章
相關標籤/搜索