💫 Vue 自定义事件
自定义事件是区别于 Vue 中的内置事件的事件,内置事件一般是提供给 HTML DOM 使用,自定义事件一般提供给组件使用的。
而当子组件需要与父组件通信,需要使用到 自定义事件。
🌰 例子 / 父组件包含控制当前组件字体大小的数据,子组件中有想要控制字体大小的按钮,可以使用自定义事件,在子组件中触发父组件中的自定义事件:
父组件中:
<div id="blog-posts-events-demo">
<div :style="{ fontSize: postFontSize + 'em' }">
<blog-post
v-for="post in posts"
v-bind:key="post.id"
v-bind:post="post"
v-on:enlarge-text="postFontSize += 0.1"
></blog-post>
</div>
</div>
2
3
4
5
6
7
8
9
10
new Vue({
el: '#blog-posts-events-demo',
data: {
posts: [/* ... */],
postFontSize: 1
}
})
2
3
4
5
6
7
子组件的按钮:
Vue.component('blog-post', {
props: ['post'],
template: `
<div class="blog-post">
<h3>{{ post.title }}</h3>
<button v-on:click="$emit('enlarge-text')">
Enlarge text
</button>
<div v-html="post.content"></div>
</div>
`
})
2
3
4
5
6
7
8
9
10
11
12
自定义事件:
适用于组件的通信: 子组件 传送到 父组件。
使用场景:A 是父组件,B 是子组件,B 想给 A 传数据,那么就要在 A 中给 B 绑定自定义事件(事件回调在父组件中);
绑定自定义事件(如在父组件):
方法一:在父组件中:
<Demo @get=“test” />
或者<Demo v-on:get="test" />
(Demo
为子组件名,给该实例对象vc
绑定 了某事件)方式二:在父组件中通过
ref
写法(灵活性强,可以使用等待修饰等。)⚠️: 两种方法都可以加上事件的修饰符(如
once
触发一次就不再触发了);⚠️⚠️: 通过
$refs
方法绑定自定义事件时,回调函数中要么配置在methods
中,要么使用箭头函数,否则this
的指向会有问题(要在模板中使用数据,只有在data
、props
、computed
中获取数据,所以可以现在data
中定义数据变量,然后在回调函数中使用this
赋值)。
触发自定义事件(在子组件):(原则:给谁绑定的事件就在谁触发(自己绑定事件只能自己触发))
this.$emit(‘get’, 数据)
,数据为可以为多个数据参数。解绑自定义事件(在子组件):
this.$off(‘get’)
解绑指定事件;this.$off([‘get’, ‘demo’])
或者this.$off()
(解绑所有) 解绑多个事件。
(上述的
get
均为自定义事件名,test
为父组件中接受数据的方法(回调函数))除了对于 HTML 上的 DOM,在组件上也可以绑定原生的 DOM 事件,但是需要使用
native
修饰符;
在 Vue 的生命周期中的
this.$destroy
也会导致组件中的自定义事件失效,原生的事件不受影响。
提示
在 Vue 的开发者工具中查看自定义事件:
🌰 自定义事件例子:
点击查看
- 在父组件
App
中,通过ref
写法绑定自定义事件:
<Student ref="student"/>
mounted() {
this.$refs.student.$on('get', this.getStudentName) // 绑定自定义事件
// this.$refs.student.$once('get', this.getStudentName) // 触发一次就不再触发的自定义事件
2
3
- 绑定的事件方法
getStudentName
:
methods: {
getStudentName(studentName, ...params) {
console.log('studentName:', studentName, params)
this.studentName = studentName // 此处的this为App组件实例
}
}
2
3
4
5
6
此处
…params
是 ES6 用法,表示可以传入多个参数。
- 在子组件
Student
中,触发事件:
首先将触发事件的事件绑定在一个按钮上,以便测试:
<button @click="sendStudentName">把学生名给App</button>
然后在 sendStudentName
中触发:
methods:{
sendStudentName() {
// 触发Student组件实例身上的get事件
this.$emit('get', this.name, 1, 2, 3)
},
}
2
3
4
5
6
- 以上步骤便完成了子组件
Student
与父组件App
之间的通信。
- 解绑自定义事件,在哪里触发在哪里解绑:
unbind(){
// 解绑一个事件
this.$off('get')
// 解绑多个自定义事件
// this.$off(['get', 'demoEvent'])
// 解绑所有自定义事件
// this.$off()
},
2
3
4
5
6
7
8
9
10
- 销毁实例,导致自定义事件失效:
death(){
this.$destroy() // 销毁了当前的Student组件的实例,销毁后所有的Student实例的自定义事件全都不奏效
}
2
3
- 在入口文件
main.js
中销毁整个 vm 实例,导致自定义事件失效:
mounted(){
setTimeout(()=>{
this.$destroy()
}, 3000)
}
2
3
4
5
(编写了一个定时器测试,在 3 秒后自定义事件失效)
🌰 绑定自定义事件的多种方法以及 this
的指向:
this
指向当前绑定自定义事件的组件:
第一种:
mounted() {
this.$refs.student.$on('get', this.getStudentName) // 绑定自定义事件
}
2
3
methods: {
getStudentName(studentName, ...params) {
console.log('studentName:', studentName, params)
this.studentName = studentName // 此处的this为App组件实例
}
}
2
3
4
5
6
在 Vue 中的
methods
中编写方法,this
会指向当前的组件。
第二种,使用箭头函数:
mounted() {
this.$refs.student.$on('get', (studentName, ...params)=>{
console.log('studentName:', studentName, params)
this.studentName = studentName // 此处的this为App实例
// 注:箭头函数没有自己的this会向外找this,此处向外为mounted()
console.log(this)
})
}
2
3
4
5
6
7
8
this
指向触发自定义事件的组件:
mounted() {
his.$refs.student.$on('get', function(studentName, ...params) {
console.log('studentName:', studentName, params)
this.studentName = studentName // 此处的this为Student组件实例(谁触发自定义事件this指向谁)
console.log(this)
})
}
2
3
4
5
6
7
🌰 在组件中绑定原生 DOM 事件的例子:
- 在
App
组件中:
<Student ref="student" @click.native="show"/>
在组件中使用绑定事件,不添加
.native
组件会默认会自定义事件,形如<Student @get="getStudentName"/>
。
methods: {
show() {
alert('you clicked Student component')
}
}
2
3
4
5
# 事件的命名
不同于 组件与 Prop,事件命名不存在任何的 大小写转换,所以触发的事件名必须要完全匹配监听这个事件所用的名称。因为在 HTML 标签中的特性又会自动转换大小写(驼峰命名无效),导致驼峰命名的事件不可能它被监听到。所以始终建议使用 kebab-case 分隔命名。
🌰 例子:
this.$emit("my-event")
<my-component v-on:my-event="doSomething"></my-component>