在表单输入组件节点上使用v-model
code_v-model、$attrs、$listeners
el: "#app-1",
data() {
return {
text: ''
}
},
components: {
'component-1': {
template: '<input type="text">'
}
}
<component-1 v-model="text"></component-1>
直接在表单组件的标签上使用v-model
,没有任何作用。
首先看看v-model
如何在原生input
元素上工作的。
data() {
return {
text: ''
}
}
<input type="text" v-bind:value="text" v-on:input="text = $event.target.value">
上面的标签为v-model
指令的完全形式,v-model
是下面代码的语法糖,通过事件处理函数与数据绑定完成双向绑定。需要注意的是事件处理函数的内容是默认的。
v-bind:value="somedata"
v-on:input="somedata = $event.target.value"
对比原生input
元素,可以得出,输入组件上的v-model
也是由事件处理与数据绑定组成。
data() {
return {
text_1: '',
text_2: ''
}
}
//......
'component-2-1': {
template: '<input type="text" v-bind:value="value" v-on:input="eventHandler">',
props: ['value'],
methods: {
eventHandler: function (event) {
this.$emit('input', event.target.value)
}
}
}
<component-2-1 v-model="text_1"></component-2-1>
<component-2-1 v-bind:value="text_1" v-on:input="text_1 = arguments[0]"></component-2-1>
输入组件模板中,监听每次输入,每次输入时就触发input
自定义事件,并以元素的值作为该事件负载;在父组件上监听这个input
自定义事件,触发该事件时执行默认的方法,将事件负载传递给父组件自己的data
;这里要关注默认的处理方法somedata = arguments[0]
,这个arguments[0]
即是自定义事件的负载;若父组件数据修改了,会通过:value=props
下发给子组件,因此子组件需要定义一个Propsvalue
,子组件还要将value
绑定在自己的input
元素上,向其他使用该组件的地方传递变化。
变化的过程: 输入 - 子组件触发自定义事件 - 父组件监听到事件 - 根据负载修改数据 - 将修改数据下发给组件 - 引起其他位置的变化
对于子组件模板上,绑定自己原生input
事件的方式,以上使用一个eventHandler
事件处理方法。我们也可以在没有参数的v-on
上绑定一个事件对象(在computed
中定义),如下:
'component-2-2': {
template: '<input type="text" v-bind:value="value" v-on="eventDict">',
props: ['value'],
computed: {
eventDict: function () {
return { input: event => this.$emit('input', event.target.value) }
}
}
}
<component-2-2 v-model="text_2"></component-2-2>
<component-2-2 v-bind:value="text_2" v-on:input="text_2 = arguments[0]"></component-2-2>
子组件props.value
用来传递变化给其他使用该输入组件的地方,如果没有它,只是单向的绑定。
使用$attrs、$listeners在多层次的组件结构中实现通信
如果在多层组件结构中,只是单纯的数据通信,那么使用$attrs、$listeners是最方便的。
在使用它们之前,先来看个选项inheritAttrs
。
data(){
return {
foo:'foo',
bar:'bar'
}
},
components:{
'component-3':{
props:['foo'],
template:'<span>{{foo}}</span>',
}
}
<dd><component-3 :foo="foo" :bar="bar"></component-3></dd>
在父组件中,我们绑定了两个Props,foo
和bar
,下发数据给子组件时,子组件只定义了foo
,bar
没地方传,组件渲染时它就留在了子组件的根元素上。
如果我们不想在子组件根元素上保留这个属性,我们可以设置选项inheritAttrs
为false。
inheritAttrs:false
使用$attrs
、$listeners
。
new Vue({
el: '#app-4',
data() {
return {
firstData: 'firstData',
secondData: 'secondData',
thirdData: 'thirdData',
fourthData: 'fourthData'
}
},
methods: {
firstEvent: function () {
console.log('第一层组件触发的事件')
},
secondEvent: function () {
console.log('第二层组件触发的事件')
},
thirdEvent:function(){
console.log('第三层组件触发的事件')
},
fourthEvent:function(){
console.log('第四层组件触发的事件')
}
},
components: {
'first': {
'props': ['firstData'],
template: '<div><h1>{{firstData}}</h1><second v-bind="$attrs" v-on="$listeners"></second></div>',
mounted() {
this.$emit('first');
},
inheritAttrs:false,
components:{
'second':{
'props':['secondData'],
template:'<div><h2>{{secondData}}</h2><third v-bind="$attrs" @fourth="eventHandler" v-on="$listeners"></third></div>',
mounted() {
this.$emit('second');
},
methods: {
eventHandler(payload){
$(this.$el).find('h2').after('<span>' + payload + '</span>')
}
},
inheritAttrs:false,
components:{
'third':{
'props':['thirdData'],
template:'<div><h3>{{thirdData}}</h3><fourth v-bind="$attrs" @fourth="eventHandler" v-on="$listeners"></fourth></div>',
mounted() {
this.$emit('third');
},
methods: {
eventHandler(payload){
$(this.$el).find('h3').after('<span>' + payload + '</span>')
}
},
inheritAttrs:false,
components:{
'fourth':{
'props':['fourthData'],
template:'<div style="background: #9FEF4E;"><h4>{{fourthData}}</h4></div>',
mounted() {
this.$emit('fourth','负载在第四层上事件上的数据');
}
}
}
}
}
}
}
}
}
})
<first :first-data="firstData" :second-data="secondData" :third-data="thirdData" :fourth-data="fourthData" @first="firstEvent"
@second="secondEvent" @third="thirdEvent" @fourth="fourthEvent"></first>
$listeners
上保存着所有低层组件触发的自定义事件,使用v-on="$listeners"
将本层及以下层触发的事件传递给上一层,上一层中可以监听$listeners
对象中所有的事件。整个过程即一个事件广播的形态。
$attrs
上保存着所有未被下发的上层组件中的数据,各层组件使用v-bind="$attrs"
向下层组件传递下发的数据。$attrs
好像一块数据蛋糕,被高层组件拿走的部分,低层组件无法再使用。配合inheritAttrs:false
可以使那些用不到的数据属性,不会保留在组件的根元素上。
v-on="$listeners"
向上广播事件,v-bind="$attrs"
向下下发数据
父子组件的引用
完整代码
示例结果是这样:
<div id="app-2">
<dl>
<dt class="first-dt">使用$parent和$refs在父子组件间传递数据</dt>
<dd>
<label>父组件</label>
<input type="text" v-model="message" @input="handle">
<span>{{message}}</span>
</dd>
<dd>
<label>子组件</label>
<component-21 ref="child"></component-21>
<span>{{message}}</span>
</dd>
</dl>
</div>
new Vue({
el:'#app-2',
data:{
message:''
},
methods:{
handle(){
this.$refs.child.message = this.message
}
},
components:{
'component-21':{
template:'<input type="text" v-model="message" @input="handle"/>',
data(){
return {message:''}
},
methods:{
handle(){
this.$parent.message = this.message
}
}
}
},
})
在子组件中可以使用$parent
获取父组件数据,也可以修改它。
在父组件中可以使用$refs
获取子组件数据,也可以修改,之前必须在视图节点上给子组件取一个名字如:ref="child"
(见组件引用 —— ref、$refs)。
就靠这两个属性,进行父子组件间的通信。
事件管理器Bus
通过该方法不仅可在任意组件内进行通信。创建一个全局的实例bus管理事件触发和监听
var bus = new Vue()
之后创建表单输入组件,在其输入事件中触发bus上的message
事件,在created
钩子中监听该事件。
Vue.component('component-a', {
template:`<input type="text" v-model="message" @input="emitEvent"/>`,
data(){
return {message:''}
},
methods:{
emitEvent(){
bus.$emit('message',this.message)
}
},
created(){
var _this = this
bus.$on('message', c_msg => {
_this.message = c_msg
})
}
})
Vue.component('component-b', {
template:`<input type="text" v-model="message" @input="emitEvent"/>`,
data(){
return {message:''}
},
methods:{
emitEvent(){
bus.$emit('message',this.message)
}
},
created(){
var _this = this
bus.$on('message', c_msg => {
_this.message = c_msg
})
}
})
new Vue({
data:{
message:''
},
methods:{
emitEvent(){
bus.$emit('message',this.message)
}
},
created(){
var _this = this
bus.$on('message', c_msg => {
_this.message = c_msg
})
}
}).$mount('#app-1')
<dl>
<dt class="first-dt">使用全局View实例管理事件,在任意组件间传递数据</dt>
<dd>
<label>父组件</label>
<input type="text" v-model="message" @input="emitEvent">
<span>{{message}}</span>
</dd>
<dd>
<label>子组件a</label>
<component-a></component-a>
<span>{{message}}</span>
</dd>
<dd>
<label>子组件b</label>
<component-b></component-b>
<span>{{message}}</span>
</dd>
</dl>
当一个组件输入时触发bus上的事件,所有组件都会监听到,并使用事件上的负载数据。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。