自定义组件的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 }”
,是没法正常工做的,由于在解析一个像这样的复杂表达式的时候,有不少边缘状况须要考虑。