模仿element-ui封裝vue元件庫(input,switch)
五、封裝一個element-ui風格的input元件
引數支援:
引數名稱 | 引數描述 | 引數型別 | 預設值 |
placeholder | 佔位符 | string | 無 |
type | 文字框型別(text/password) | string | text |
disabled | 禁用 | boolean | false |
clearable | 是否顯示清空按鈕 | boolean | false |
show-password | 是否顯示密碼切換按鈕 | boolean | false |
name | name屬性 | string | 無 |
事件支援:
事件名稱 | 事件描述 |
blur | 失去焦點事件 |
change | 內容改變事件 |
focus | 獲取焦點事件 |
5.1input元件的基本框架和樣式以及處理placeholde、type、name、disabled
因為這部分與前面介紹的內容相同且比較簡單,所以將這部分放在一起,不多做介紹了。
這裡需要注意的是,disabled屬性為true時,輸入框禁用,並且需要改變樣式,之前在button元件封裝的時候也用到了相同的方法,獲取到值後動態設定元件樣式。
input元件的框架以及樣式,獲取到的資料以及資料處理:
<template> <div class="ra-input"> <input class="ra-input_inner" :class="{'is-disabled': disabled}":placeholder="placeholder" :type="type" :name="name" :disabled="disabled"> </div> </template> <script> export default { name: 'ra-input', components: { }, props: { placeholder: { type: String, default: '' }, type: { type: String, default: 'text' }, name: { type: String, default:'' }, disabled: { type: Boolean, default: false } }, data () { return {} }, methods: {} } </script> <style lang="scss" scoped> .ra-input{ width: 100%; position: relative; font-size: 14px; display: inline-block; .ra-input_inner{ -webkit-appearance: none; background-color: #fff; background-image: none; border: 1px solid #dcdfe6; border-radius: 4px; box-sizing: border-box; color: #606266; display: inline-block; font-size: inherit; height: 40px; line-height: 40px; outline: none; padding: 0 15px; transition: border-color .2s cubic-bezier(.645,045,.355,1); width: 100%; &:focus{ outline: none; border-color: #409eff; } // input禁用樣式 &.is-disabled{ background-color: #f5f7fa; border-color: #e4e7ed; color: #c0c4cc; cursor:not-allowed; } } } </style>
父元件中傳值也是與之前一樣的:
<ra-input placeholder="請輸入密碼" type="password" name="name" disabled=true></ra-input>
5.2v-model語法糖——實現雙向資料繫結
當我們給一個input標籤進行雙向資料繫結時,我們需要使用value繫結資料,再使用input事件監聽標籤內資料的變動,如下:
<input :value="username" @input="username=$event.target.value"/>
在封裝input元件時,這樣顯然是不合適的,所以這裡我們需要使用到v-model語法糖。
顯然,我們是不能給我們封裝的input元件直接使用v-model繫結資料的,但是由於v-model的特性,他將value值繫結在了元件上,所以,我們元件內部通過接收value值的方式,就可以接收到傳入的資料;並且v-model也實現了input事件,在元件內部繫結的input事件作為回撥,把value值返回給父元件,這樣就實現了input元件的雙向綁定了。
父元件中的使用v-model繫結:
<ra-input v-model="username"></ra-input>
元件內部繫結value值以及實現回撥:
//繫結value值和input事件 <input class="ra-input_inner" :class="{'is-disabled': disabled}" :placeholder="placeholder" :type="type" :name="name" :value="value" @input="handleInput" :disabled=disabled> //繫結input事件進行回撥 handleInput (e) { this.$emit('input', e.target.value) }
5.3實現clearable功能和showPassword功能
當我們在元件中鍵入clearable屬性時,我們希望元件可以有一個一鍵刪除資料得功能。
當input元件的type屬性是password時,我們希望在給與show-password屬性時,可以有一個顯示和隱藏密碼的按鈕。
實現這個兩個功能,除了基本的父子元件傳值外,還要新增i標籤的icon字型圖示,以及實現功能。
<div class="ra-input" :class="{'ra-input_suffix':showSuffix}"> <input class="ra-input_inner" :class="{'is-disabled': disabled}" :placeholder="placeholder" :type="type" :name="name" :value="value" @input="handleInput" :disabled=disabled> <span class="ra-input_suffix"> <i class="on-input_icon ra-icon-cancel" v-if="clearable && value" @click="clear"></i> <i class="on-input_icon ra-icon-visible" v-if="showPassword && type=='password'" @click="handlePassword"></i> </span> </div>
樣式:
.ra-input_suffix{
.ra-input_inner{
padding-right: 30px;
}
.ra-input_suffix{
position: absolute;
right: 10px;
height: 100%;
top: 0;
line-height: 40px;
text-align: center;
color: #c0c4cc;
transition: all .3s;
z-index: 900;
i{
color: #c0c4cc;
font-size: 14px;
cursor: pointer;
transition: color .2s cubic-bezier(.645,.045,.355,1);
}
}
}
5.3.1實現clearable功能
首先獲取父元件傳遞的clearable值,然後給i標籤繫結一個點選事件,這個事件觸發input事件回撥,當點選叉號字型圖示時,將父元件的value清空:
clear () {
this.$emit('input', '')
}
5.3.2實現showPassword功能
實現showPassword功能的思路很簡單,就是改變input的type型別即可。但是,我們不能直接改變父元件傳遞過來的type值,但是我們可以使用判斷type值的方式,實現type的改變。
首先設定一個布林型別的變數,並且設定點選事件改變這個變數:
data () {
return {
// 顯示是否顯示密碼框
passwordVisible: false
}
},
methods: {
handlePassword () {
this.passwordVisible = !this.passwordVisible
}
}
然後我們需要在繫結type值時,進行兩重判斷。
第一步、判斷showPassword是否為真;第二步、如果為真則通過passwordVisible去判斷type為text還是password,以此來控制隱藏和現實,否則type值就為傳入的type值即可:
:type="showPassword ? (passwordVisible ? 'text' : 'password') : type"
--------------------------------------------------至此,input元件封裝完成-------------------------------------------------
附元件全部程式碼:
<template> <div class="ra-input" :class="{'ra-input_suffix':showSuffix}"> <input class="ra-input_inner" :class="{'is-disabled': disabled}" :placeholder="placeholder" :type="showPassword ? (passwordVisible ? 'text' : 'password') : type" :name="name" :disabled="disabled" :value="value" @input="handlerInput"> <span class="ra-input_suffix" v-if="showSuffix"> <i class="ra-input_icon ra-icon-clear" v-if="clearable && value" @click="clear"></i> <i class="ra-input_icon ra-icon-visible" v-if="showPassword && type=='password'" @click="handlePassword"></i> </span> </div> </template> <script> export default { name: 'ra-input', props: { placeholder: { type: String, default: '' }, type: { type: String, default: 'text' }, name: { type: String, default: '' }, disabled: { type: String, default: null }, value: { type: String, default: null }, clearable: { type: Boolean, default: false }, showPassword: { type: Boolean, default: true } }, data () { return { // 顯示是否顯示密碼框 passwordVisible: false } }, computed: { showSuffix () { return this.clearable || this.showPassword } }, methods: { handlerInput (e) { this.$emit('input', e.target.value) }, clear () { this.$emit('input', '') }, handlePassword () { this.passwordVisible = !this.passwordVisible } } } </script> <style lang="scss" scoped> .ra-input{ width: 100%; position: relative; font-size: 14px; display: inline-block; .ra-input_inner{ -webkit-appearance: none; background-color: #fff; background-image: none; border: 1px solid #dcdfe6; border-radius: 4px; box-sizing: border-box; color: #606266; display: inline-block; font-size: inherit; height: 40px; line-height: 40px; outline: none; padding: 0 15px; transition: border-color .2s cubic-bezier(.645,045,.355,1); width: 100%; &:focus{ outline: none; border-color: #409eff; } // input禁用樣式 &.is-disabled{ background-color: #f5f7fa; border-color: #e4e7ed; color: #c0c4cc; cursor:not-allowed; } } } .ra-input_suffix{ .ra-input_inner{ padding-right: 30px; } .ra-input_suffix{ position: absolute; right: 10px; height: 100%; top: 0; line-height: 40px; text-align: center; color: #c0c4cc; transition: all .3s; z-index: 900; i{ color: #c0c4cc; font-size: 14px; cursor: pointer; transition: color .2s cubic-bezier(.645,.045,.355,1); } } } </style>
六、封裝一個element-ui風格的switch元件
引數支援:
引數名 | 引數描述 | 引數型別 | 預設值 |
v-model | 雙向繫結 | boolean | false |
name | name屬性 | string | text |
activeColor | 自定義啟用的顏色 | string | #1ec63b |
inactiveColor | 自定義不啟用的眼神 | string | #dd001b |
事件支援:
事件名稱 | 事件描述 |
change | change時觸發 |
6.1switch元件的基本框架和樣式
switch元件基本框架:
<template> <div class="-switch"> <span class="ra-switch_core"> <span class="ra-switch_button"></span> </span> </div> </template>
switch元件樣式:
<style lang="scss" scoped> .ra-switch{ display: inline-block; align-items: center; position: relative; font-size: 14px; line-height: 20px; vertical-align: middle; .ra-switch_core{ margin: 0; display: inline-block; position: relative; width: 40px; height: 20px; border: 1px solid #dcdfe6; outline: none; border-radius: 10px; box-sizing: border-box; background: #dcdfe6; cursor: pointer; transition: border-color .3s,background-color .3s; vertical-align: middle; .ra-switch_button{ position:absolute; top: 1px; left: 1px; border-radius: 100%; transition: all .3s; width: 16px; height: 16px; background-color: #fff; } } } </style>
6.2實現switch元件的資料雙向繫結
實現switch元件資料雙向繫結和input元件相同,使用v-model語法糖即可。
在父元件種通過v-model繫結資料,在元件內部獲取value屬性,並且定義一個回撥函式與父元件通訊,改變父元件中的繫結值即可。
父元件:
<ra-switch v-model="active" ></ra-switch>
子元件,點選時改變is-checked類狀態,觸發滑塊滑動:
<div class="ra-switch" :class="{'is-checked':value}" @click="handleClick"> <span class="ra-switch_core"> <span class="ra-switch_button"></span> </span> </div> methods: { handleClick () { this.$emit('input', !this.value) } }
滑動樣式:
// 選中樣式
.is-checked {
.ra-switch_core{
border-color: #409eff;
background-color: #409eff;
.ra-switch_button {
transform: translateX(20px);
}
}
}
6.3實現switch元件顏色自定義
自定義switch元件的顏色,首先需要傳入顏色的值,在子元件中獲取後,使用ref獲取節點,將背景顏色改變為對應顏色即可。
父元件傳遞色彩引數:
<ra-switch v-model="active" active-color="#13ce66" inactive-color="#ff4949" ></ra-switch>
子元件中定義ref="core"以確定節點:
<div class="ra-switch" :class="{'is-checked':value}" @click="handleClick"> <span class="ra-switch_core" ref="core"> <span class="ra-switch_button"></span> </span> </div>
通過mouted鉤子和watch監聽,在剛進入頁面以及value改變時對顏色進行改變:
mounted () {
// 修改開關顏色
if (this.activeColor || this.inactiveColor) {
var color = !this.value ? this.activeColor : this.inactiveColor
this.$refs.core.style.borderColor = color
this.$refs.core.style.backgroundColor = color
}
},
watch: {
'value' (e) {
// 修改開關顏色
if (this.activeColor || this.inactiveColor) {
var color = !e ? this.activeColor : this.inactiveColor
this.$refs.core.style.borderColor = color
this.$refs.core.style.backgroundColor = color
}
}
}
6.4name屬性支援
使用者在使用switch元件的時候,實質上是當成表單元素來使用的。因此可能會用到元件的name屬性。所以需要在switch元件中新增一個checkbox,並且當值改變的時候,也需要設定checkbox的value值。
加入input標籤:
<template> <div class="ra-switch" :class="{'is-checked':value}" @click="handleClick"> <span class="ra-switch_core" ref="core"> <span class="ra-switch_button"></span> </span> <input type="checkbox" class="ra-switch_input" :name="name" ref="input"> </div> </template>
設定標籤樣式,因為input標籤只作為name繫結使用,所以將其隱藏起來:
// 隱藏input標籤
.ra-switch_input{
position:absolute;
width: 0;
height: 0;
opacity: 0;
margin: 0;
}
我們在頁面載入和點選時修改input的checked值,保證可以和value值同步:
mounted () {
// 修改開關顏色
if (this.activeColor || this.inactiveColor) {
var color = !this.value ? this.activeColor : this.inactiveColor
this.$refs.core.style.borderColor = color
this.$refs.core.style.backgroundColor = color
}
// 控制checkbox的值,input值同步value值
this.$refs.input.checked = this.value
},
methods: {
handleClick () {
this.$emit('input', !this.value)
// 控制checkbox的值,input值同步value值
this.$refs.input.checked = this.value
}
}
--------------------------------------------------------------至此,switch元件封裝完成--------------------------------------------------
附switch元件程式碼:
<template> <div class="ra-switch" :class="{'is-checked':value}" @click="handleClick"> <input type="checkbox" class="ra-switch_input" :name="name" ref="input"> <span class="ra-switch_core" ref="core"> <span class="ra-switch_button"></span> </span> </div> </template> <script> export default { name: 'ra-switch', props: { value: { type: Boolean, default: false }, activeColor: { type: String, default: '' }, inactiveColor: { type: String, default: '' }, name: { type: String, default: '' } }, mounted () { // 修改開關顏色 if (this.activeColor || this.inactiveColor) { var color = !this.value ? this.activeColor : this.inactiveColor this.$refs.core.style.borderColor = color this.$refs.core.style.backgroundColor = color } // 控制checkbox的值,input值同步value值 this.$refs.input.checked = this.value }, watch: { 'value' (e) { // 修改開關顏色 if (this.activeColor || this.inactiveColor) { var color = !e ? this.activeColor : this.inactiveColor this.$refs.core.style.borderColor = color this.$refs.core.style.backgroundColor = color } } }, methods: { handleClick (e) { this.$emit('input', !this.value) // 控制checkbox的值,input值同步value值 this.$refs.input.checked = this.value } } } </script> <style lang="scss" scoped> .ra-switch{ display: inline-block; align-items: center; position: relative; font-size: 14px; line-height: 20px; vertical-align: middle; .ra-switch_core{ margin: 0; display: inline-block; position: relative; width: 40px; height: 20px; border: 1px solid #dcdfe6; outline: none; border-radius: 10px; box-sizing: border-box; background: #dcdfe6; cursor: pointer; transition: border-color .3s,background-color .3s; vertical-align: middle; .ra-switch_button{ position:absolute; top: 1px; left: 1px; border-radius: 100%; transition: all .3s; width: 16px; height: 16px; background-color: #fff; } } } // 選中樣式 .is-checked { .ra-switch_core{ border-color: #409eff; background-color: #409eff; .ra-switch_button { transform: translateX(20px); } } } // 隱藏input標籤 .ra-switch_input{ position:absolute; width: 0; height: 0; opacity: 0; margin: 0; } </style>