1. 程式人生 > 其它 >模仿element-ui封裝vue元件庫(button)

模仿element-ui封裝vue元件庫(button)

例項內容

封裝常見的功能元件(Button,Modal,Form相關),封裝完成後封裝成UI元件庫釋出到NPM上。

涉及知識點

vue基礎語法
元件基本語法
字鍵通訊(sync,provide,inject)
插槽使用
prop校驗
過渡與動畫處理
計算屬性與監聽屬性
v-model語法糖
vue外掛機制
npm釋出

例項目的

掌握元件封裝的語法和技巧學會造輪子,瞭解元件庫實現原理搭建和積累自己的元件庫學習前提有一定vue基礎,懂vue語法熟悉ES6的一些常見語法對vue感興趣

----------------------------------------------------------------------------分割線---------------------------------------------------------------------------------

一、使用vue腳手架初始化一個專案

使用vue created rainbow-ui,建立一個名為rainbow-ui的專案。

按照自己的習慣設定腳手架風格,這裡不多做介紹。

腳手架搭建完畢後,將App.vue檔案下的自帶內容清理一下,為後續開發做準備。

二、如何封裝,註冊和使用一個元件

在componet下建立一個button.vue的檔案,放置button元件程式碼。建立一個組建的button元件,,並且指定name為RaButton。

<template>
<button class="ra-button">
按鈕元件
</button>
</template>

<script>

export default {
name: 'ra-button'
}

</script>

<style lang="scss">

</style>


建立元件完成後,不能在專案中直接使用,需要到main.js中註冊才可以使用。

import Vue from 'vue'
import App from './App.vue'
// 第一步:匯入button元件
import RaButton from './components/button.vue'

Vue.config.productionTip = false

// 第二步:註冊元件,設定(元件名,元件)
Vue.component(RaButton.name, RaButton)

new Vue({
render: h => h(App)
}).$mount('#app')
註冊完成後,元件就可以在專案中使用了。

<template> <div> <ra-button></ra-button> </div> </template>

按鈕效果:

元件最簡單的封裝,註冊和使用方法就是這樣一個流程。

三、封裝一個element-ui風格的按鈕

需要使用到的知識:

元件通訊
元件插槽
props校驗
引數支援:

引數名   

引數描述 引數型別 預設值
type  按鈕型別(primary/success/warning/danger/info) string default
plain  是否為樸素按鈕 boolean false
round  是否圓角按鈕 boolean false
circle 是否圓形按鈕 boolean false
disabled 是否禁用 boolean false
icon 圖示型別 string


事件支援:

事件名

 事件描述
click 點選事件


使用插槽:

簡單來說,凡是希望元件中內容可以靈活設定的地方,都需要用到slot插槽來自定義內容。

使用slot來定義按鈕上的文字內容:

<template>
<button class="-button">
<span><slot></slot></span>
</button>
</template>
在使用時就可以直接輸入文字,定義按鈕文字內容了:

<template>
<div>
<ra-button>登入</ra-button>
<ra-button>刪除</ra-button>
<ra-button>取消</ra-button>
</div>
</template>

效果:

button元件基礎樣式:

<style lang="scss">
.ra-button{
display: inline-block;
line-height: 1;
white-space: nowrap;
cursor: pointer;
background: #ffffff;
border: 1px solid #dcdfe6;
color: #606266;
-webkit-appearance: none;
text-align: center;
box-sizing: border-box;
outline: none;
margin: 0;
transition: 0.1s;
font-weight: 500;
//禁止元素的文字被選中
-moz-user-select: none;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
padding: 12px 20px;
font-size: 14px;
border-radius: 4px;
&:hover,
&:hover{
color: #409eff;
border-color: #c6e2ff;
background-color: #ecf5ff;
}
}
</style>


效果:

3.1button元件的type屬性

讓按鈕支援type屬性,使得按鈕支援不同樣式:

第一步:父元件元件傳遞type屬性

<div id="app">
    <div class="row">
      <ra-button plain>按鈕</ra-button>
      <ra-button plain type="primary">primary按鈕</ra-button>
      <ra-button plain type="success">success按鈕</ra-button>
      <ra-button plain type="info">info按鈕</ra-button>
      <ra-button plain type="danger">danger按鈕</ra-button>
      <ra-button plain type="warning">warning按鈕</ra-button>
    </div>
</div>


第二步:子元件接收負元件傳遞的資料

export default {
name: 'ra-button',
// 此時對props進行校驗,值接收string型別的type值
props: {
type:{
type: String,
// 設定預設值:如果不傳值,那麼使用default
default: 'default'
}
},
created () {
console.log(this.type)//defalut primary success info danger warning
}
}


此時對type進行校驗,如果傳遞了非String型別的值(這裡我傳遞了123),將會報錯。

failed for prop "type". Expected String with value "123", got Number with value 123.


第三步:通過繫結類名的方法動態控制樣式

<template>
<button class="ra-button" :class="`ra-button-${type}`">
<span><slot></slot></span>
</button>
</template>


第四步:設定不同型別的樣式

.ra-button-primary{
  color:#fff;
  background-color: #409eff;
  border-color: #409eff;
  &:hover,
  &:focus{
    background: #66b1ff;
    background-color: #66b1ff;
    color: #fff;
    }
  }
  .ra-button-success{
  color:#fff;
  background-color: #67c23a;
  border-color: #67c23a;
  &:hover,
  &:focus{
    background: #85ce61;
    background-color: #85ce61;
    color: #fff;
    }
  }
  .ra-button-info{
  color:#fff;
  background-color: #909399;
  border-color: #909399;
  &:hover,
  &:focus{
    background: #a6a9ad;
    background-color: #a6a9ad;
    color: #fff;
    }
  }
  .ra-button-warning{
  color:#fff;
  background-color: #e6a23c;
  border-color: #e6a23c;
  &:hover,
  &:focus{
    background: #ebb563;
    background-color: #ebb563;
    color: #fff;
    }
  }
  .ra-button-danger{
  color:#fff;
  background-color: #f56c6c;
  border-color: #f56c6c;
  &:hover,
  &:focus{
    background: #f78989;
    background-color: #f78989;
    color: #fff;
    }
  }

第五步:至此就完成了對於按鈕樣式的設定,檢視一下效果

3.2button元件的plain屬性

和type型別相同,我們只要將樣式先設定好,然後通過父元件傳遞過來的值進行判斷,就可以設定plain屬性了。

第一步:父元件元件傳遞plain值

<template>
<div id="app">
<div class="row">
<ra-button plain>按鈕</one-button>
<ra-button plain type="primary">primary按鈕</ra-button>
<ra-button plain type="success">success按鈕</ra-button>
<ra-button plain type="info">info按鈕</ra-button>
<ra-button plain type="danger">danger按鈕</ra-button>
<ra-button plain type="warning">warning按鈕</ra-button>
</div>
</div>
</template>


第二步:子元件接收負元件傳遞的資料,同樣進行props校驗,並且設定預設值為false

props: {
    plain: {
        type: Boolean,
        default: false
    }
}
        

第三步:通過繫結類名的方法動態控制樣式,由於plain型別是布林值,所以在型別中我們使用物件的形式來控制樣式

<template>
    <button class="ra-button" :class="[`ra-button-${type}`,{
'is-plain':plain
}]">
    <span><slot></slot></span>
    </button>
</template>

第四步:設定不同型別的樣式,由於plain型別是以物件的形式在類中定義的,所以使用獲取屬性的方法定義樣式

 // 樸素按鈕樣式
.ra-button.is-plain{
  &:hover,
  &:focus{
    background: #fff;
    border-color: #489eff;
    color: #409eff;
  }
}
.ra-button-primary.is-plain{
  color: #409eff;
  background: #ecf5ff;
  &:hover,
  &:focus{
    background: #409eff;
    border-color: #409eff;
    color: #fff;
  }
}
.ra-button-success.is-plain{
  color: #67c23a;
  background: #c2e7b0;
  &:hover,
  &:focus{
    background: #67c23a;
    border-color: #67c23a;
    color: #fff;
  }
}
.ra-button-info.is-plain{
  color: #909399;
  background: #d3d4d6;
  &:hover,
  &:focus{
    background: #909399;
    border-color: #909399;
    color: #fff;
  }
}
.ra-button-warning.is-plain{
  color: #e6a23c;
  background: #f5dab1;
  &:hover,
  &:focus{
    background: #e6a23c;
    border-color: #e6a23c;
    color: #fff;
  }
}
.ra-button-danger.is-plain{
  color: #f56c6c;
  background: #fbc4c4;
  &:hover,
  &:focus{
    background: #f56c6c;
    border-color: #f56c6c;
    color: #fff;
  }
}

第五步:至此就完成了對於按鈕樣式的設定,檢視一下效果

3.3button元件的round屬性

設定round屬性和之前的相似,只要在元件中定義好了樣式,動態獲取屬性值即可。

獲取屬性值:

round: {
type: Boolean,
default: false
}

round樣式:

.ra-button.is-round{
border-radius: 20px;
padding: 12px 23px;
}

效果圖:

3.4button元件的circle屬性

circle同樣是上面的方法,樣式為:

.ra-button.is-circle{
border-radius: 50%;
padding: 12px;
}

3.5button元件中使用字型圖示
在專案中使用字型圖示,首先需要有字型圖示,我們可以去阿里巴巴向量圖示庫下載。

下載完成後,在asset目錄下新建一個fonts目錄,存放我們下載到的字型圖示。

做完準備工作後,我們就可以開始把字型圖示運用到專案中了。

第一步:在main.js中引入字型圖示

import './assets/fonts/iconfont.css'

第二步:將下載的字型圖示css檔案中的類名做修改,我將icon全部改為了ra-icon,並且將初始的iconfont類改為了[class*='ra-icon'],當類名中有ra-icon時使用,如下

[class*='ra-icon'] {
font-family: "iconfont" !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.ra-icon-bluetoothoff:before {
content: "\e697";
}

第三步:父元件傳遞圖示名,子元件接收並且放到圖示中

父元件傳值:

<div class="row">
<ra-button plain icon='ra-icon-delete' circle></ra-button>
<ra-button plain icon='ra-icon-delete' circle type="primary"></ra-button>
<ra-button plain icon='ra-icon-delete' circle type="success"></ra-button>
<ra-button plain icon='ra-icon-delete' circle type="info"></ra-button>
<ra-button plain icon='ra-icon-message' circle type="danger"></ra-button>
<ra-button plain icon='ra-icon-check' circle type="warning"></ra-button>
</div>

子元件接收:

icon: {
type: String,
default: ''
}

使用接收到的字型圖示。在沒有傳入icon時隱藏<i>標籤,在slot插槽沒有傳入值時,不顯示<span>標籤

<template>
<button class="ra-button" :class="[`ra-button-${type}`,{
'is-plain':plain,
'is-round':round,
'is-circle':circle,
}]">
<i v-if="icon" :class="`ra-icon-${icon}`"></i>
<!-- 如果沒傳入文字插槽,則不顯示span內容 -->
<span v-if="$slots.default"><slot></slot></span>
</button>
</template>

第四步:設定icon配套樣式,使圖示和文字之間有一定間隔

.ra-button [class*=ra-icon-]+span{
margin-left: 5px;
}

第五步:檢視效果

3.6button元件中的點選事件

我們在使用元件時,直接給元件定義事件是不會被觸發的。我們需要在元件中定義一個點選事件,這個點選事件不進行其他操作,只出發父元件中的點選事件。

元件中的定義點選事件:

<template>
<button class="ra-button" :class="[`ra-button-${type}`,{
'is-plain':plain,
'is-round':round,
'is-circle':circle,
}]"
@click="handleClick"
>
<i v-if="icon" :class="`ra-icon-${icon}`"></i>
<!-- 如果沒傳入文字插槽,則不顯示span內容 -->
<span v-if="$slots.default"><slot></slot></span>
</button>
</template>

定義一個點選事件,這個點選事件的作用是呼叫父元件中的點選事件,並且回撥

methods: {
handleClick (e) {
this.$emit('click', e)
}
}

父元件在使用時定義自己的點選事件,其本質是子元件中的點選事件觸發父元件中的點選事件。

<div class="row">
<ra-button @click="getInfo">按鈕</ra-button>
</div>
methods: {
getInfo () {
console.log('獲取資訊!!')//獲取資訊!!
}
}

3.7button元件中的disabled屬性

和之前相似,只要父子元件傳值並且動態獲取這個值並且賦給disabled屬性,並且設定一個disabled樣式即可。

 <div class="row">
<ra-button @click="getInfo" disabled>按鈕</ra-button>
</div>
<template>
<button class="ra-button" :class="[`ra-button-${type}`,{
'is-plain':plain,
'is-round':round,
'is-circle':circle,
'is-disabled':disabled
}]"
@click="handleClick"
:disabled="disabled"
>
<i v-if="icon" :class="`ra-icon-${icon}`"></i>
<span v-if="$slots.default"><slot></slot></span>
</button>
</template>
disabled: {
type: Boolean,
default: false
}

disabled樣式:

.ra-button.is-disabled{
cursor: no-drop;
}

------------------------------------------------------------------至此,按鈕元件封裝完成!------------------------------------------------------------------

附元件程式碼:

<template>
  <button class="ra-button" :class="[`ra-button-${type}`,{'is-plain':plain,'is-round':round,'is-circle':circle,'is-disabled':disabled}]"   @click="handleClick" :disabled="disabled"
>
    <i v-if="icon" :class="icon"></i>
    <!-- 如果沒傳入文字插槽,則不顯示span內容 -->
    <span v-if="$slots.default">
      <slot></slot>
    </span>
  </button>
</template>

<script>

export default {
  name: 'ra-button',
  props: {
    type: {
      type: String,
      // 設定預設值:如果不傳值,那麼使用default
      default: 'default'
    },
    plain: {
      type: Boolean,
      default: false
    },
    round: {
      type: Boolean,
      default: false
    },
    circle: {
      type: Boolean,
      default: false
    },
    icon: {
      type: String,
      default: ''
    },
    disabled: {
      type: Boolean,
      default: false
    }
  },
  methods: {
    handleClick (e) {
      this.$emit('click', e)
    }
  }
}
</script>

<style lang="scss" scope>
.ra-button{
    display: inline-block;
    line-height: 1;
    white-space: nowrap;
    cursor: pointer;
    background: #ffffff;
    border: 1px solid #dcdfe6;
    color: #606266;
    -webkit-appearance: none;
    text-align: center;
    box-sizing: border-box;
    outline: none;
    margin: 0;
    transition: 0.1s;
    font-weight: 500;
    //禁止元素的文字被選中
    -moz-user-select: none;
    -webkit-user-select: none;
    -moz-user-select: none;
    -ms-user-select: none;
    padding: 12px 20px;
    font-size: 14px;
    border-radius: 4px;
    &:hover,
    &:hover{
      color: #409eff;
      border-color: #c6e2ff;
      background-color: #ecf5ff;
    }
}

.ra-button-primary{
  color:#fff;
  background-color: #409eff;
  border-color: #409eff;
  &:hover,
  &:focus{
    background: #66b1ff;
    background-color: #66b1ff;
    color: #fff;
    }
  }
  .ra-button-success{
  color:#fff;
  background-color: #67c23a;
  border-color: #67c23a;
  &:hover,
  &:focus{
    background: #85ce61;
    background-color: #85ce61;
    color: #fff;
    }
  }
  .ra-button-info{
  color:#fff;
  background-color: #909399;
  border-color: #909399;
  &:hover,
  &:focus{
    background: #a6a9ad;
    background-color: #a6a9ad;
    color: #fff;
    }
  }
  .ra-button-warning{
  color:#fff;
  background-color: #e6a23c;
  border-color: #e6a23c;
  &:hover,
  &:focus{
    background: #ebb563;
    background-color: #ebb563;
    color: #fff;
    }
  }
  .ra-button-danger{
  color:#fff;
  background-color: #f56c6c;
  border-color: #f56c6c;
  &:hover,
  &:focus{
    background: #f78989;
    background-color: #f78989;
    color: #fff;
    }
  }
  // 樸素按鈕樣式
.ra-button.is-plain{
  &:hover,
  &:focus{
    background: #fff;
    border-color: #489eff;
    color: #409eff;
  }
}
.ra-button-primary.is-plain{
  color: #409eff;
  background: #ecf5ff;
  &:hover,
  &:focus{
    background: #409eff;
    border-color: #409eff;
    color: #fff;
  }
}
.ra-button-success.is-plain{
  color: #67c23a;
  background: #c2e7b0;
  &:hover,
  &:focus{
    background: #67c23a;
    border-color: #67c23a;
    color: #fff;
  }
}
.ra-button-info.is-plain{
  color: #909399;
  background: #d3d4d6;
  &:hover,
  &:focus{
    background: #909399;
    border-color: #909399;
    color: #fff;
  }
}
.ra-button-warning.is-plain{
  color: #e6a23c;
  background: #f5dab1;
  &:hover,
  &:focus{
    background: #e6a23c;
    border-color: #e6a23c;
    color: #fff;
  }
}
.ra-button-danger.is-plain{
  color: #f56c6c;
  background: #fbc4c4;
  &:hover,
  &:focus{
    background: #f56c6c;
    border-color: #f56c6c;
    color: #fff;
  }
}
.ra-button.is-round{
  border-radius: 20px;
  padding: 12px 23px;
}

.ra-button.is-circle{
  border-radius: 50%;
  padding: 12px;
}

.ra-button.is-disabled{
   cursor: no-drop;
}
.ra-button [class*=ra-icon-]+span{
  margin-left: 5px;
}
</style>