自定義組件的v-modelhtml
首先咱們先說一下在自定義組件中使用v-model的必要條件vue
這樣講可能會有點難理解,仍是上代碼吧...vuex
<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>
這是最終實現效果須要必備的,看完這些代碼若是你是小白,你可能會有點不理解爲何要這樣作,下面我告訴你原理。app
首先在咱們使用的v-model
中,其內部實現的原理就是一個 value屬性和一個input事件,其主要步驟就是,用v-bind綁定value,而後用input事件監聽值的變化,當文本框中的值發生變化的時候,input事件就會觸發,那麼咱們能夠在input事件中獲取到改變後的值而後賦值給value,這樣是否是就完成了雙向數據綁定了。上代碼:this
<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事件,spa
因此有了以下代碼:雙向綁定
<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>
了。code
$listeners的使用component
由來:當咱們在項目開發過程當中會出現不少組件嵌套的關係,那麼若是還要在最外層的組件向內部傳遞數據的話,有以下幾種方式:htm
而 $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的出現。
根據上面我說的那些需求,咱們寫一下代碼:
<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
的方式
<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 }」
,是沒法正常工做的,由於在解析一個像這樣的複雜表達式的時候,有不少邊緣狀況須要考慮。