自定义事件
自定义事件有能力使子组件事件触发父组件中的动作。
那么父组件如何监听事件呢?可以使用指令v-on:event-name="callback"
监听。
而子组件又是如何触发事件的呢? 很简单,调用this.$emit(eventName)
。
先来个简单例子
new Vue({
el:'#app-1',
methods:{
callback:function(){
alert('父组件监听到事件触发,执行回调。')
}
},
components: {
"component-1":{
template:'<button v-on:click="emitor" class="btn btn-info">\
子组件模板:点击触发自定义事件</button>',
methods:{
emitor:function(){
console.log('111')
this.$emit('alert-event')
}
}
}
}
})
<div id="app-1">
<component-1 v-on:alert-event="callback"></component-1>
</div>
根据以上代码,可以得出个事件执行流程,基本上不管什么自定义事件都是这个流程
- 子组件某方法
this.$emit('event')
- DOM上
v-on
监听event
- 父组件
methods
中的callback
子组件已经和它外部完全解耦了。它所做的只是报告自己的内部事件,因为父组件可能会关心这些事件。
事件负载
在事件执行的同时,子组件还可以在触发事件的同时挂上一些负载的数据,随着事件传递给父组件。
使用API的第二个参数this.$emit(event, payload)
,具体如下。
new Vue({
el: '#app-2',
data: {
message: ''
},
methods: {
handleMessage: function (payload) {
this.message = payload.message
}
},
components: {
"component-2": {
data: function () {
return { message: '' }
},
template: '\
<div>\
<input type="text" v-model="message" class="form-control w-25 mt-2">\
<button @click="sendMsg" class="btn btn-info mt-2">发送</button>\
</div>',
methods: {
sendMsg: function () {
this.$emit('message', { message: this.message })
}
}
}
}
})
<div id="app-2">
<input v-model="message" class="form-control w-25">
<component-2 @message="handleMessage"></component-2>
</div>
点击发送按钮触发事件并把信息传递给父组件,可以看到还是这里同样遵循自定义事件流程,其他都是烟雾,之多了个负载this.$emit('message', { message: this.message })
原生事件
通过.native
后缀还可以在子组件元素根节点上绑定原生事件的监听。
new Vue({
el:'#app-3',
methods:{
todo:function(){
alert('事件回调方法是父组件中的')
}
},
components:{
"component-3":{
template:'<button class="btn btn-info mt-2">原生事件监听</button>'
}
}
})
<div id="app-3">
<component-3 @click.native="todo"></component-3>
</div>
还是要千万注意,回调方法在父组件作用域上。
双向绑定(.sync修饰符与update事件)
可以通过以下步骤实现一个双向绑定:
- 在子组件VirtualDOM节点上,通过
v-bind:prop.sync="foo"
进行数据下发,这里以.sync
修饰符标注,通知父组件子组件需要进行props
的更新。 - 子组件通过
this.$emit("update:prop",newValue)
通知父组件自己需要把prop
更新为一个新值newValue
(以子组件的data
选项作为过渡变量) - 父组件会监听这个更新事件,从而在负载上拿到
newValue
,更新foo
的数据,并把新数据重新下发给子组件的prop
注意:这里并不是子组件props
值的改变引发父组件数据的改变,而是利用子组件的data
做桥梁,通过事件及其负载引起父组件的变动。
new Vue({
el: '#app-4',
data: { parentMsg: 'parent\'s message' },
components: {
"component-4": {
props: ['child_msg'],
data: function () {
return { inputText: this.child_msg }
},
template: '\
<div>\
<input v-model="inputText" class="form-control form-control-sm w-25" type="text">\
</div>',
watch: {
child_msg: function (val) {
this.inputText = val
},
inputText: function (val) {
this.$emit('update:child_msg', val)
}
}
}
}
})
<div id="app-4">
<input v-model="parentMsg" class="form-control form-control-sm w-25" type="text">
<component-4 :child_msg.sync="parentMsg" class="mt-2"></component-4>
</div>
父、子组件各包含一个输入框,并且将它们绑定到自己的某data
属性上。watch
子组件的该data
属性,一有输入就触发事件通知父组件,并payload
新值。父组件通过payload
更新自己的data
,并通过prop
将新值下发给子组件,子组件watch
自己的prop
,prop
一旦变动,将新变动赋给data
。
理解v-model
在input元素上使用
我们在用输入框时,会用v-model
进行双向绑定。
<input v-mode="message">
等价于<input :value="message" @input="message = $event.target.value" />
具体行为:① 在input
的value
属性上引用组件的data
② 发生oninput
事件时,更新组件data
,从而更新value
。
new Vue({
el:'#app-6',
data:{
message:"Hello"
}
})
<div id="app-6">
<input v-model="message"/>
<input :value="message" @input="message = $event.target.value" />
<div>{{message}}</div>
</div>
在自定义事件的表单输入组件上使用
前提:<myComponent v-model="price">
等价于<myComponent :value="price" @input="price = arguments[0]" >
new Vue({
el: '#app-7',
data: {
price: 100
},
components: {
"component-7": {
props: ['value'],
template: '\
<div>\
<input :value="value" @input="updateValue($event.target.value)" class="form-control form-control-sm w-25">\
<slot></slot>\
<div><span class="badge badge-info">子组件Prop:【{{value}}】</span></div>\
</div>',
methods:{
updateValue:function(value){
value = value+"-"
this.$emit('input',value)
}
}
}
},
})
<div id="app-7">
<!-- <component-7 v-model="price"></component-7> -->
<component-7 :value="price" @input="price = arguments[0]" >
<div>
<span class="badge badge-info">父组件数据:【{{price}}】</span>
</div>
</component-7>
</div>
这里的流程,输入框输入时,触发子组件上input
事件并执行updateValue
方法,方法参数为输入框中的value
(通过$emit.target.value
获取),方法可以先对value
进行一系列加工处理形成super_value
,最后使用this.$emit('input',super_value)
触发父组件在子组件节点上监听的input
事件,并将加工过的super_value
负载在事件上。父组件@input="price = arguments[0]"
中的arguments[0]
即是这个super_value
,父组件通过input
的回调更新自己的data
,在将data
下发给子组件的value
特性。
这里如果使用v-model
指令,那么子组件特性value
、父组件监听的事件input
及其回调price = arguments[0]
, 这些都是固定的,不能变化。
此示例的执行流程其实与双向绑定(.sync修饰符与update事件)中的例子是一样的。
只是这里父组件上的input
事件功效没有双向绑定中update
事件那么强大。
在组件上使用
在输入框组件中已经说过v-model
的种种限制,其中最主要的两点,下发的组件特性必须命名为value
和父组件监听的只能绑定事件input
,不灵活, 例如在checkbox
中,我要给下发的特性取名为checked
代替value
,并且父组件不想监听@input
事件,而是@change
事件。
为了解决这个不灵活的问题,可以在组件model
选项设置prop
和event
,如下
new Vue({
el: '#app-9',
data: {
isChecked: false,
message:'please choose this box'
},
components: {
'component-9': {
model: {
prop: 'checked',
event: 'change'
},
props:{
checked:Boolean,
value:String
},
template: '<div><input type="checkbox" @click="choose($event.target.checked)"><slot></slot></div>',
methods:{
choose:function(checked){
this.$emit('change',checked)
}
}
}
}
})
<div id="app-9" style="height:500px">
<component-9 v-model="isChecked" v-bind:value="message">
<span class="badge badge-info">特性`value`现在从v-model绑定中解放出来了,可自定义使用</span>
</component-9>
</div>
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。