模仿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>