1. 問(wèn)題
Vue的項(xiàng)目中有如下的代碼共啃,需要根據(jù)不同字典的數(shù)據(jù)去展示下拉組件:
<template>
<el-select v-model="product.carId" placeholder="請(qǐng)選擇車(chē)型 >
<el-option
v-for="item in options"
:key="item.id"
:label="item.name"
:value="item.id"
></el-option>
</el-select>
</template>
<script>
export default {
data() {
return {
options: Utils.get('PRODUCT_TYPE')
}
}
}
</script>
問(wèn)題在于很多component中都需要用到這樣的代碼片段,而且很多都是重復(fù)的某個(gè)字典鸿脓,書(shū)寫(xiě)麻煩,代碼冗余创泄。
2. 分析
Vue的官方文檔提供了組件封裝的一些介紹。下面是摘自官方文檔的說(shuō)明:
自定義事件也可以用于創(chuàng)建支持 v-model 的自定義輸入組件肋僧。記装呤ぁ:
<input v-model="searchText">
等價(jià)于:
<input :value="searchText" @input="searchText = $event.target.value">
當(dāng)用在組件上時(shí),v-model 則會(huì)這樣:
<custom-input :value="searchText" @input="searchText = $event"></custom-input>
注意:當(dāng)用在組件上時(shí)嫌吠,v-model 則會(huì)這樣
這個(gè)句話有一些隱含信息
我們先假定要封裝一個(gè)簡(jiǎn)單的 input 的組件止潘,你覺(jué)得如下的代碼能達(dá)到目的嗎?
// 調(diào)用代碼
<tsp-input v-model="name"></tsp-input>
//組件定義代碼
<template>
<input type="text" v-model="value">
</template>
<script>
export default {
props: ['value']
}
</script>
不妨自己加些輸出展示辫诅,可以看到凭戴,組件定義中的值改變了,并不會(huì)改變外部的值炕矮,也就是說(shuō)并不管用么夫。進(jìn)一步分析:
v-model
等同于 :value @input
,是區(qū)別對(duì)待的肤视。先明確一個(gè)概念档痪,directive
是 vue
所有用到 v-
屬性的類(lèi)型的統(tǒng)稱(chēng),所有帶 v-
類(lèi)型的元素其實(shí)都是在跟directive
打交道邢滑,directive
再跟底層的原生的元素打交道腐螟。
(1) 當(dāng)v-model
被使用在 input
中的時(shí)候
input
其實(shí)是directiveInput
(自己隨意取名),來(lái)自原生input
的數(shù)據(jù),會(huì)被隱式地將數(shù)據(jù) 通過(guò) $emit
傳遞給 directiveInput
乐纸,對(duì)此 directiveInput
的默認(rèn)接收到數(shù)值的行為是:searchText = $event.target.value
衬廷。
(2)當(dāng)它被使用在自定義的組件中的時(shí)候
我們?cè)?tsp-input
中使用 v-model
,那么數(shù)據(jù)的流轉(zhuǎn)是 原生input
到 directiveInput
汽绢,再到 directiveTspInput
吗跋,directiveInput
和 directiveTspInput
都有接收值的默認(rèn)行為,但是不具備往外繼續(xù)傳遞的行為庶喜。即 tsp-input
雖然掛了input
的事件小腊,但是無(wú)法被觸發(fā)。這點(diǎn)不同于 vue
介入 原生input
將值傳遞給了 directiveInput
久窟。
繼續(xù)往外面?zhèn)髦抵雀裕仨氾@示的聲明傳值過(guò)程,如下:
// 調(diào)用代碼
<tsp-input v-model="name"></tsp-input>
//組件定義代碼
<template>
<input type="text" :value="value" @input="$emit('input', $event.target.value)">
</template>
<script>
export default {
props: ['value']
}
3. 應(yīng)用
假定我們需要基于 el-select 封裝得到一個(gè) tsp-select斥扛,代碼大概是這樣的入问,實(shí)用有效:
//實(shí)際調(diào)用
<tsp-select v-model="product.cardId" dictName="PRODUCT_TYPE"></tsp-select>
// tsp-select.vue 封裝
<template>
<el-select :value="product.carId" @input="$emit('input', $event)"
placeholder="請(qǐng)選擇車(chē)型>
<el-option
v-for="item in options"
:key="item.id"
:label="item.name"
:value="item.id"
></el-option>
</el-select>
</template>
<script>
export default {
data() {
return {
options: Utils.get(this.dictName)
}
}
}
</script>
4. 總結(jié)和完整代碼
使用 ElementUI
的 el-select
能夠成功使用 v-model
,說(shuō)明 原生input
到 directiveInput
再到directiveElSelect
的數(shù)據(jù)流轉(zhuǎn)是ok的(即在el-select
代碼內(nèi)部是有封裝 $emit('input', $event)
的)稀颁,那么到了我們的 tsp-select
中芬失,想要也能被直接使用,也需要將值繼續(xù)往外傳遞匾灶。如下是完整封裝代碼:
tsp-select.vue
<template>
<el-select :value="value" @input="$emit('input', $event)" placeholder="請(qǐng)選擇">
<el-option
v-for="item in options"
:key="item.code"
:label="item.text"
:value="item.code"
></el-option>
</el-select>
</template>
<script>
export default {
props: {
value: [Number, String],
dictName: String,
},
data() {
return {
options: G.D.get(this.dictName)
}
},
}
</script>
和代碼調(diào)用
<el-form-item label="產(chǎn)品類(lèi)型" prop="carId">
<tsp-select v-model="product.cardId" dictName="PRODUCT_TYPE"></tsp-select>
</el-form-item>