最近在重構(gòu)公司項(xiàng)目代碼,因?yàn)橹斑@一塊不是我寫的,所以也抽空看了一下之前同事的代碼,可能是因?yàn)榇蠹叶际浅鯇W(xué)VUE,現(xiàn)在看來,有些代碼還是略顯稚嫩的,有一個(gè)小組件,雖然很簡(jiǎn)單,但是還是可以拿出來說一下的,因?yàn)樗且粋€(gè)雙向綁定組件.
v-model
是什么
教程上說的很清楚,v-model
是一個(gè)語法糖,它是由 :value
和 @input
構(gòu)成的[1]
為什么我需要v-model
使用v-model
不僅僅是因?yàn)樗?jiǎn)單,它的意義一方面是簡(jiǎn)化了書寫,二是簡(jiǎn)化了邏輯,三是更加語義化,四是讓API更加友好,簡(jiǎn)單易懂.
在VUE2.5之后,.sync
修飾符又重新回歸了,我們可以通過.sync
修飾符來處理雙向綁定問題,但是我覺得兩者的使用場(chǎng)景是不一樣的,v-model更多的是接收用戶輸入,而.sync
更多的是同步數(shù)據(jù).
怎么樣實(shí)現(xiàn)一個(gè)可以使用v-model
的組件
其實(shí)這個(gè)問題也很簡(jiǎn)單,只要仔細(xì)思考了上面兩個(gè)問題,應(yīng)該可以知道如何寫,下面我就拿項(xiàng)目中的這個(gè)例子來做一個(gè)介紹,這個(gè)組件的效果是這樣的,一個(gè)簡(jiǎn)單的switch
開關(guān):
改造之前的代碼:
<template>
<div class="switch-outer"
:class="{'on':on,'off':!on}"
@click="handleChange">
<span class="text"
v-show="on">{{onText}}</span>
<span class="text"
v-show="!on">{{offText}}</span>
<span class="active-ball"></span>
</div>
</template>
<script>
export default {
data() {
return {
on: this.isOn
};
},
props: {
onText: {
type: String,
default: '開啟'
},
offText: {
type: String,
default: '關(guān)閉'
},
isOn: {
type: Boolean,
default: true
}
},
methods: {
handleChange() {
this.on = !this.on;
this.$emit('change', this.on);
}
},
watch: {
isOn() {
this.on = this.isOn;
}
}
};
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
.switch-outer {
position: absolute;
cursor: pointer;
color: #ffffff;
.active-ball {
width: 28px;
height: 28px;
border-radius: 50%;
background-color: currentColor;
position: absolute;
top: 1px;
}
}
.on {
background: #00a4ee;
.active-ball {
left: 53px;
transition: all 0.5s ease;
}
.text {
margin-left: 14px;
font-size: 14px;
}
}
.off {
background: #c0ccda;
.active-ball {
left: 2px;
transition: all 0.5s ease;
}
.text {
margin-left: 44px;
font-size: 14px;
}
}
</style>
改造之前的代碼,并不能使用v-model
來做雙向綁定,但是很顯然,這是一個(gè)switch
開關(guān),是一個(gè)用戶輸入組件,所以使用v-model
是很自然的需求,但是目前這個(gè)組件的使用方式是這樣的
<sky-switch class="share-switch"
@change="handleChange"
:isOn="group.is_files_share"
ref="shareSwitch"></sky-switch>
// 手動(dòng)監(jiān)聽change事件,修改數(shù)據(jù)
handleChange(on) {
this.group.is_files_share = on;
}
我們需要自己監(jiān)聽change
事件,手動(dòng)的修改值,這顯然不符合我們對(duì)用戶輸入組件的認(rèn)知,我們需要一個(gè)像<input>那樣的組件,通過 v-model
來綁定數(shù)據(jù),剩下的交給vue
來幫助我們處理.
顯然這種使用方式并不友好,不能稱為一個(gè)合格的組件.如果沒有文檔,我需要看源碼才能知道這個(gè)組件如何使用,如果源碼寫的十分復(fù)雜,這個(gè)組件基本上是不可復(fù)用的.
接下來我們就開始魔改這個(gè)組件代碼,刪除不必要的監(jiān)聽和屬性,簡(jiǎn)單直接的利用v-model
來實(shí)現(xiàn)這個(gè)功能
改造后的代碼
<template>
<div class="switch-outer"
:class="{'on':on,'off':!on}"
@click="handleChange">
<span class="text"
v-show="on">{{onText}}</span>
<span class="text"
v-show="!on">{{offText}}</span>
<span class="active-ball"></span>
</div>
</template>
<script>
export default {
props: {
onText: {
type: String,
default: '開啟'
},
offText: {
type: String,
default: '關(guān)閉'
},
value: { // 將 isOn改成 value ,用來接收 v-model 的傳值
type: Boolean,
default: true
}
},
methods: {
handleChange() {
this.on = !this.on; // 通過賦值操作來觸發(fā)屬性 on 的 set 方法
this.$emit('change', this.on); // 發(fā)送 change 事件,作為一個(gè)事件鉤子,方便用戶處理自己的邏輯
}
},
computed: {
on: { // 去掉了data里面的on,使用計(jì)算屬性
get() { // get時(shí)返回 value值
return this.value;
},
set(val) { // set時(shí)發(fā)送 input 事件
this.$emit('input', val);
}
}
}
};
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
.switch-outer {
position: absolute;
cursor: pointer;
color: #ffffff;
.active-ball {
width: 28px;
height: 28px;
border-radius: 50%;
background-color: currentColor;
position: absolute;
top: 1px;
transition: all 0.5s ease;
}
transition: all 0.5s ease;
}
.on {
background: #00a4ee;
.active-ball {
left: 53px;
transition: all 0.5s ease;
}
.text {
margin-left: 14px;
font-size: 14px;
transition: all 0.5s ease;
}
}
.off {
background: #c0ccda;
.active-ball {
left: 2px;
}
.text {
margin-left: 44px;
font-size: 14px;
transition: all 0.5s ease;
}
}
</style>
這段代碼的核心是那個(gè)計(jì)算屬性on
,巧妙的通過計(jì)算屬性的get
和set
方法,實(shí)現(xiàn)監(jiān)聽數(shù)據(jù)變化,get
時(shí)返回通過props
傳入的value
,set
時(shí),向父組件發(fā)送input
事件,這樣就可以配合v-model
來使用了,因?yàn)?code>v-model的本質(zhì)就是向組件傳一個(gè)value
的prop
,并且監(jiān)聽input
方法[2]
style
部分做了微調(diào),因?yàn)檫@里加入了動(dòng)畫,但是原來的動(dòng)畫只有部分值是動(dòng)的,會(huì)顯得很突兀,所以調(diào)整為所有變化的屬性都有動(dòng)畫,這樣整個(gè)動(dòng)畫變得很平滑.
改造后的組件,使用方式是這樣的:
<sky-switch class="share-switch" v-model="group.share_file"></sky-switch>
簡(jiǎn)單明了,一看就是一個(gè)用戶輸入組件,和<input>
元素一樣.
當(dāng)然,這個(gè)組件還不夠完善,API
接口也沒有很好的設(shè)計(jì),但是用來說明v-model
這個(gè)問題應(yīng)該是足夠了.
寫在最后
沒事多重構(gòu)自己的點(diǎn),也可以重構(gòu)別人的代碼,看看別人是怎么實(shí)現(xiàn)的,自己又能怎么實(shí)現(xiàn),相互對(duì)比,學(xué)習(xí),這樣才能快速進(jìn)步.
前兩天有事情,清了一天假,項(xiàng)目進(jìn)度有點(diǎn)落后,今天過來補(bǔ)點(diǎn)進(jìn)度,剩下點(diǎn)時(shí)間,寫了這篇文字,雖然是一個(gè)很小的點(diǎn),但是,希望能對(duì)對(duì)個(gè)別人有所啟發(fā).
-
這里只是簡(jiǎn)單的說,其實(shí)由什么屬性和事件組成,可以通過一個(gè)
model
參數(shù)來定制 ? -
這里依然是從簡(jiǎn)單的角度來說. ?