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

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