目录

💫 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>
1
2
3
4
5
6
7
8
9
10
new Vue({
  el: '#blog-posts-events-demo',
  data: {
    posts: [/* ... */],
    postFontSize: 1
  }
})
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>
  `
})
1
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 的指向会有问题(要在模板中使用数据,只有在 datapropscomputed 中获取数据,所以可以现在 data 中定义数据变量,然后在回调函数中使用 this 赋值)。

  • 触发自定义事件(在子组件):(原则:给谁绑定的事件就在谁触发(自己绑定事件只能自己触发)) this.$emit(‘get’, 数据) ,数据为可以为多个数据参数。

  • 解绑自定义事件(在子组件):

    • this.$off(‘get’) 解绑指定事件;
    • this.$off([‘get’, ‘demo’]) 或者 this.$off() (解绑所有) 解绑多个事件。

    (上述的 get 均为自定义事件名, test 为父组件中接受数据的方法(回调函数))

  • 除了对于 HTML 上的 DOM,在组件上也可以绑定原生的 DOM 事件,但是需要使用 native 修饰符;

在 Vue 的生命周期中的 this.$destroy 也会导致组件中的自定义事件失效,原生的事件不受影响。

提示

在 Vue 的开发者工具中查看自定义事件:

image-20220403130046723

🌰 自定义事件例子

点击查看
  • 在父组件 App 中,通过 ref 写法绑定自定义事件:
<Student ref="student"/>
1
mounted() {
    this.$refs.student.$on('get', this.getStudentName) // 绑定自定义事件
    // this.$refs.student.$once('get', this.getStudentName) // 触发一次就不再触发的自定义事件
1
2
3
  • 绑定的事件方法 getStudentName
methods: {
  getStudentName(studentName, ...params) {
      console.log('studentName:', studentName, params)
      this.studentName = studentName // 此处的this为App组件实例
    }
}
1
2
3
4
5
6

此处 …params 是 ES6 用法,表示可以传入多个参数。

  • 在子组件 Student 中,触发事件:

首先将触发事件的事件绑定在一个按钮上,以便测试:

<button @click="sendStudentName">把学生名给App</button>
1

然后在 sendStudentName 中触发:

methods:{
  sendStudentName() {
      // 触发Student组件实例身上的get事件
      this.$emit('get', this.name, 1, 2, 3)
    },
}
1
2
3
4
5
6
  • 以上步骤便完成了子组件 Student 与父组件 App 之间的通信。

  • 解绑自定义事件,在哪里触发在哪里解绑:
unbind(){
  // 解绑一个事件
  this.$off('get')

  // 解绑多个自定义事件
  // this.$off(['get', 'demoEvent'])

  // 解绑所有自定义事件
  // this.$off()
},
1
2
3
4
5
6
7
8
9
10
  • 销毁实例,导致自定义事件失效:
death(){
  this.$destroy() // 销毁了当前的Student组件的实例,销毁后所有的Student实例的自定义事件全都不奏效
}
1
2
3

  • 在入口文件 main.js 中销毁整个 vm 实例,导致自定义事件失效:
mounted(){
    setTimeout(()=>{
        this.$destroy()
    }, 3000)
}
1
2
3
4
5

(编写了一个定时器测试,在 3 秒后自定义事件失效)

🌰 绑定自定义事件的多种方法以及 this 的指向:

  • this 指向当前绑定自定义事件的组件:

第一种:

mounted() {
  this.$refs.student.$on('get', this.getStudentName) // 绑定自定义事件
}
1
2
3
methods: {
  getStudentName(studentName, ...params) {
  	console.log('studentName:', studentName, params)
  	this.studentName = studentName // 此处的this为App组件实例
  }
}
1
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)
    })
}
1
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)
    })
}
1
2
3
4
5
6
7

🌰 在组件中绑定原生 DOM 事件的例子:

  • App 组件中:
<Student ref="student" @click.native="show"/>
1

在组件中使用绑定事件,不添加 .native 组件会默认会自定义事件,形如 <Student @get="getStudentName"/>

methods: {
  show() {
      alert('you clicked Student component')
  }
}
1
2
3
4
5

# 事件的命名

不同于 组件与 Prop,事件命名不存在任何的 大小写转换,所以触发的事件名必须要完全匹配监听这个事件所用的名称。因为在 HTML 标签中的特性又会自动转换大小写(驼峰命名无效),导致驼峰命名的事件不可能它被监听到。所以始终建议使用 kebab-case 分隔命名。

🌰 例子:

this.$emit("my-event")
1
<my-component v-on:my-event="doSomething"></my-component>
1
📢 上次更新: 2022/09/02, 10:18:16