用過vuejs的前端工程師,對於v-model一定印象深刻。它向類似textarea,input等原生html原生添加雙向數據綁定的能力非常方便。但是對於你的定制vue組件並不是能夠直接應用v-model的,我們需要做一些額外的工作,但是這個額外工作是非常簡單的。
為了理解如何給你的組件提供v-momeiydel支持,你應該深入地了解v-model本身底層是如何工作地。v-model初看起來就像是一個魔術,但是真的沒有那么神秘。
v-model="syncedProp"
等價於以下代碼
:value="syncedProp" @input="syncedProp=$arguments[0] 或者
:value="syncedProp" @input="syncedProp=$event.target.value
理解了以上地兩點,那么如果需要支持v-model,你自己的組件需要做的就是:
1. 接收一個:value的property
2. 當用戶變更value值的時候發送一個@input事件
基礎實現:
我們假設我們有一個date picker的組件,該組件接收year,month的value:{month:1,year:2019},我們希望該組件有兩個輸入Inputs,一個用於month,一個用於year,並且通過v-model來更新綁定的對象.這里是實現的例子代碼:
<template> <div class="date-picker"> Month: <input type="number" ref="monthPicker" :value="value.month" @input="updateDate()"/> Year: <input type="number" ref="yearPicker" :value="value.year" @input="updateDate()"/> </div> </template> <script> export default { props: ['value'], methods: { updateDate() { this.$emit('input', { month: +this.$refs.monthPicker.value, year: +this.$refs.yearPicker.value }) } } }; </script>
有了以上的實現代碼后,我們就可以像下面的代碼來使用它:
<template> <div class="wrapper"> <date-picker v-model="date"></date-picker> <p> Month: {{date.month}} Year: {{date.year}} </p> </div> </template> <script> import DatePicker from './DatePicker.vue'; export default { components: { DatePicker }, data() { return { date: { month: 1, year: 2017 } } } }) </script>
總結以上范例代碼,我們可以看到就做了兩件事:
1. 接受一個value這個prop,並通過該value prop傳入的值在本組件中使用
2. 當需要通知外部數據已經變化需要更新外部的綁定數據時,只需要emit一個input事件即可!
高級一點的例子
在上面基礎版本基礎上,我們把事情搞得稍微復雜一點,比如,我們傳入的日期格式不是object形式,而時字符串形式mm/yyyy的格式,這時需要使用的化,必須要做進一步處理,同樣地,當用戶主動修改了數據修正外部地數據時,也需要再組合成一個字符串emit出去!
<template> <div class="date-picker"> Month: <input type="number" ref="monthPicker" :value="splitDate.month" @input="updateDate()"/> Year: <input type="number" ref="yearPicker" :value="splitDate.year" @input="updateDate()"/> </div> </template> <script> export default { props: ['value'], computed: { splitDate() { const splitValueString = this.value.split('/'); return { month: splitValueString[0], year: splitValueString[1] } } }, methods: { updateDate() { const monthValue = this.$refs.monthPicker.value; const yearValue = this.$refs.yearPicker.value; this.$emit('input', `${monthValue}/${yearValue}`); } } }; </script>
定制v-model所使用的prop和event
如上面所說,v-model匹配的就是value屬性以及input的事件上報,但是我們也可以修改這個默認的行為。
其辦法就是通過在我們的定制組件中,聲明一個model對象,該model對象包含兩個字段,一個prop,一個event分別用於對應的value和input事件
比如下面的組件代碼:
export default { prop: ['cprop'], model: { prop: 'cprop', event: 'cevent' } methods: { handleInput (value) { this.$emit('cevent', value) } } }
如果我們在html中像下面來使用:
<basic-input v-model="email" /> <!-- 等價於以下代碼 --> <basic-input :cprop="email" @cevent="e => email = e.target.value" />