自定義組件 1. 自定義組件的v model 首先我們先說一下在自定義組件中使用v model的必要條件 在自定義的組件中要有input(這裡我們先不討論單選覆選框) 在自定義組件的模板對象中要有props屬性,且裡面要含有一個value 在自定義組件的input標簽上要綁定value屬性值為pro ...
自定義組件
自定義組件的v-model
首先我們先說一下在自定義組件中使用v-model的必要條件
- 在自定義的組件中要有input(這裡我們先不討論單選覆選框)
- 在自定義組件的模板對象中要有props屬性,且裡面要含有一個value
- 在自定義組件的input標簽上要綁定value屬性值為props中傳入的值,且還需要發出一個input事件
這樣講可能會有點難理解,還是上代碼吧...
<div id="app"> <child-com v-model="message"></child-com> <span>{{ message }}</span> </div> <template id="childCom"> <div> <input type="text" :value="value" @input='inputEvent'> </div> </template> <script> const childCom = { template: '#childCom', props: ['value'], methods: { inputEvent(event) { this.$emit('aaa', event.target.value) } }, } const vm = new Vue({ el: '#app', data: { message: '可以雙向綁定的了' }, components: { childCom } }) </script>
這是最終實現效果需要必備的,看完這些代碼如果你是小白,你可能會有點不理解為什麼要這樣做,下麵我告訴你原理。
首先在我們使用的
v-model
中,其內部實現的原理就是一個 value屬性和一個input事件,其主要步驟就是,用v-bind綁定value,然後用input事件監聽值的變化,當文本框中的值發生變化的時候,input事件就會觸發,那麼我們可以在input事件中獲取到改變後的值然後賦值給value,這樣是不是就完成了雙向數據綁定了。上代碼:<div id="app"> <input type='text' :value='message' @input='inputEvent'> <span>{{ message }}</span> </div> <script> const vm = new Vue({ el: '#app', data: { message: '可以雙向綁定的了' }, methods: { inputEvent(event) { this.message = event.target.value; } } }) </script>
就這樣幾個步驟,就達到了v-model的效果了,這就是他的原理,然後讓我們深一步想,讓自定義組件使用雙向數據綁定。因為我們知道其內部就是value和input事件,
所以有瞭如下代碼:
<div id="app"> <child-com :value='message' @input='message=$event'></child-com> <!-- 此代碼就這裡和最開始代碼不同 --> <span>{{ message }}</span> </div> <template id="childCom"> <div> <input type="text" :value="value" @input='inputEvent'> </div> </template> <script> const childCom = { template: '#childCom', props: ['value'], methods: { inputEvent(event) { this.$emit('input', event.target.value) } }, } const vm = new Vue({ el: '#app', data: { message: '可以雙向綁定的了' }, components: { childCom } }) </script>
根據上面的原理,現在你應該知道了為什麼要傳一個value在子組件了吧,明白之後,您就可以把
<child-com :value='message' @input='message=$event'></child-com>
替換成<child-com v-model="message"></child-com>
了。$listeners的使用
由來:當我們在項目開發過程中會出現很多組件嵌套的關係,那麼如果還要在最外層的組件向內部傳遞數據的話,有如下幾種方式:
- 從父向子傳遞,子再向孫傳遞,一直傳遞下去,那麼最裡面的組件想往最外層傳東西則可以從最裡面向外面逐層$emit發送出去,但是仔細想想,一個簡單的傳遞信息,卻涉及到了這之間所有的組件,而他們只是一個中間者,這讓代碼維護起來非常困難
- 使用vuex來進行傳遞,這樣確實方便了很多,但是這樣做如果沒有其他用處的話就有點大材小用了
- 使用事件匯流排,這樣使用也不容易維護
而
$listeners
和$attrs
的出現,就完美的解決了第一種情況的發生<div id="app"> <child-com :name='name' :age='age' @test-listeners='testListeners'></child-com> </div> <script> const vm = new Vue({ el: '#app', // 父組件 data: { name: 'lyl', age: 20, }, methods: { testListeners(arg) { console.log(arg) } }, components: { childCom: { // 子組件 inheritAttrs: false, template: ` <div> <span> {{name}} </span> <grand-com v-bind='$attrs' v-on='$listeners'></grand-com> </div> `, props: ['name'], components: { grandCom: { // 孫子組件 inheritAttrs: false, template: ` <div> <span @click='listenClick'>{{$attrs.age}}</span> </div> `, methods: { listenClick() { this.$emit('test-listeners','aaaaaaa'); } }, } } } } }) </script>
上面代碼中,孫子組件要發出一個是將讓父組件調用,這個時候我們可以在中間過渡的子組件模板使用的孫子組件上綁定這個屬性,即:
v-on='$listeners'
,這樣一來父組件就能直接調用孫子組件發出的方法了,並且在中間層的子組件上並沒有什麼多餘的部分.sync的使用方法
我們都知道,在一個組件上,我們只能使用一個v-model,但是如果我們的組件中有多個input標簽呢,並且每個input標簽中的值都不同,且每個都想進行雙向綁定,這個時候,v-model就不行了。於是乎就出現了.sync的出現。
根據上面我說的那些需求,我們寫一下代碼:
- 不使用.sync的代碼
<div id="app"> <child-com :value='obj.value' @aaa='obj.value = $event' :name='obj.name' @bbb='obj.name = $event' :age='obj.age' @ccc='obj.age = $event'> </child-com> <p>{{ obj }}</p> <p>{{ obj.value }}</p> <p>{{ obj.name }}</p> <p>{{ obj.age }}</p> </div> <!-- childCom組件的模板 --> <template id="childCom"> <div> <input type="text" :value="value" @input='inputValueEvent'> <br> <input type="text" :value="name" @input='inputNameEvent'> <br> <input type="text" :value="age" @input='inputAgeEvent'> </div> </template> <script> const childCom = { template: '#childCom', props: ['value','name','age'], methods: { inputValueEvent(event) { this.$emit('aaa', event.target.value) }, inputNameEvent(event) { this.$emit('bbb', event.target.value) }, inputAgeEvent(event) { this.$emit('ccc', event.target.value) } }, } const vm = new Vue({ el: '#app', data: { obj: { value: '雙向綁定' , name: 'coderlyl' , age: 20} }, components: { childCom } }) </script>
根據上面的代碼,我們不難發現,我們在
標簽中書寫了過多的重覆的東西,可讀性也不是很好,下麵我們再使用 .sync
的方式- 使用.sync的代碼
<div id="app"> <child-com v-bind:value.sync='obj.value' v-bind:name.sync="obj.name" v-bind:age.sync="obj.age"> <!--這裡也發生了變化--> </child-com> <p>{{ obj }}</p> <p>{{ obj.value }}</p> <p>{{ obj.name }}</p> <p>{{ obj.age }}</p> </div> <template id="childCom"> <div> <input type="text" :value="value" @input='inputValueEvent'> <br> <input type="text" :value="name" @input='inputNameEvent'> <br> <input type="text" :value="age" @input='inputAgeEvent'> </div> </template> <script> const childCom = { template: '#childCom', props: ['value','name','age'], methods: { inputValueEvent(event) { this.$emit('update:value', event.target.value) // 這裡發生了變化 }, inputNameEvent(event) { this.$emit('update:name', event.target.value) // 這裡發生了變化 }, inputAgeEvent(event) { this.$emit('update:age', event.target.value) // 這裡發生了變化 } }, } const vm = new Vue({ el: '#app', data: { message: '可以雙向綁定的了', obj: { value: '雙向綁定' , name: 'coderlyl' , age: 20} }, components: { childCom } }) </script>
也許看完這裡,你並沒有覺得好到哪裡去了,下麵還有更簡單的寫法
<child-com v-bind.sync="obj"></child-com> <!-- 其他代碼一樣 -->
對,沒錯!這是終極簡化版,但是這隻針對於對象才能用
註意帶有
.sync
修飾符的v-bind
不能和表達式一起使用 (例如v-bind:title.sync=”doc.title + ‘!’”
是無效的)。取而代之的是,你只能提供你想要綁定的屬性名,類似v-model
。將
v-bind.sync
用在一個字面量的對象上,例如v-bind.sync=”{ title: doc.title }”
,是無法正常工作的,因為在解析一個像這樣的複雜表達式的時候,有很多邊緣情況需要考慮。