在封装组件时,为了让组件的使用者可以监听到组件内状态的变化,此时需要用到组件的自定义事件。
比如这样的情况:
上面这些场景,需要父组件和子组件之间进行通讯,即:子组件通过抛出事件通知父组件。这就用到组件的自定义事件:一种组件间通信的方式,适用于:子组件 ==> 父组件
一个子组件可以抛出很多事件,属性可以一层接一层向下传递,事件可以一层接一层向上抛
① 封装组件时 — 声明自定义事件
② 封装组件时 — 触发自定义事件
③ 使用组件时 — 监听自定义事件
组件内封装的自定义事件,必须事先在 emits 节点中声明,才能有效抛出事件,有两种实现方式:
方式一:直接使用 $emit 抛出事件
<!-- 子组件:必须在 emits 节点中声明自定义事件 -->
<template>
<div> {{count}} </div>
<!-- $emit 是 vue 的内置函数,它可有 1~n 个参数:第一个为必须,表示抛出的事件名称(父组件接收使用);第二个之后均表示事件的参数 -->
<div @click="$emit('change',123)">子组件抛出事件</div>
</template>
<script>
export default{
name: 'MyCounter',
data() { return { count: 1 } },
emits: ['change'], //必须声明自定义事件,抛出事件才有效
}
<script>
方式二:先触发子组件 methods 中的方法,在方法内通过 this.$emit('自定义事件名称', 可选参数),向父组件抛出自定义事件
<template>
<div> {{count}} </div>
<!-- 先触发 methods 中的方法,在方法内抛出自定义事件 -->
<button @click="add">数字+1</button>
</template>
<script>
export default{
name: 'MyCounter',
data() { return { count: 1 } },
emits: ['change'], //必须声明自定义事件,抛出事件才有效
methods: {
add() {
this.count++
this.$emit("change"); //抛出自定义事件
}
}
}
<script>
在使用自定义组件时,父组件需要注册自定义事件,可以通过 v-on 的形式监听自定义事件,格式为:@自定义事件名称,比如上面的案例中,抛出了 change 事件,使用就是:@change
<!-- 父组件:监听子组件内的 change 自定义事件 -->
<MyCounter @change="getmsg"></MyCounter>
<script>
export default{
getmsg(){
console.log("监听到了组件自定义事件")
}
}
</script>
自定义事件传参
在调用 this.$emit() 方法触发自定义事件时,可以通过第 2 个参数(以及之后的所有参数)为自定义事件传参
<!-- 子组件:定义自定义事件 -->
<script>
export default{
name: 'MyCounter',
data() { return { count: 1 } },
emits: ['change'], //声明自定义事件
methods: {
add() {
this.count++
this.$emit("change", this.count, '第二个参数', '第三个参数'); //触发自定义事件,并传递参数
}
}
}
<script>
向外传递的数据,可以通过事件处理函数的形参直接接收到
<!-- 父组件:监听子组件的自定义事件 -->
<MyCounter @change="getmsg"></MyCounter>
<script>
export default{
methods: {
// 接收子组件传递的形参
getmsg(arguments1, arguments2, arguments3){
console.log("监听到了组件自定义事件")
console.log(arguments1, arguments2, arguments3)
}
}
}
</script>
通过自定义事件修改组件中参数的值
vue 提供了 v-model 这个双向数据绑定的指令,它可以对表单元素进行数据绑定,快速渲染和获取表单元素的值。
当需要维护组件内外数据的同步时,同样可以在组件标签上使用 v-model 指令

注意:emits 中有两个参数,第一个是“update:属性名”,第二个时要修改的值
通过案例说明
比如:点击子组件中的按钮,改变父组件中的值
① 在子组件中,通过 props 接收父组件传递的参数,再通过 emits 抛出自定义事件,emits 中有两个参数,第一个是“update:属性名”,第二个时要修改的值
<!-- 子组件:ChildData.vue -->
<template>
<div>子组件:{{ name }}</div>
<button @click="update">修改</button>
</template>
<script>
export default {
name: 'ChildData ',
//通过 props 属性接收传父组件传递的值
props: ['name'],
//在 emits 中声明自定义事件,事件名称中必须有一个固定前缀"update:",要更新哪个属性,就把属性名写到 update 后面
emits: ['update:name'],
methods: {
update() {
this.$emit("update:name", "Lucy") //触发 update 事件时,携带的参数用于修改父组件中对应参数的值
}
}
}
</script>
② 父组件通过 v-model 绑定参数,并监听参数的变化,当点击子组件中的按钮时,触发自定义事件,name 的值被修改
<!-- 父组件:ParentData.vue -->
<template>
<div>父组件:{{ name }}</div>
<!-- 使用子组件,通过 v-model 绑定参数 -->
<ChildData v-model:name="name" />
</template>
<script>
import ChildData from 'ChildData .vue';
export default {
name: 'ParentData',
components: {
ChildData
}
data() {
return {
name: 'Tom' //父组件传递的参数
}
}
}
</script>
通过自定义事件,实事传递 input 中的值
<!-- 父组件 -->
<template>
<div class="main">
<div>{{ nm }}</div>
<!-- 父组件中通过v-model绑定value值 -->
<input-data title="名称" name="name" placeholder="请输入名称" v-model:value="nm"></input-data>
</div>
</template>
<script>
import InputData from "./InputData.vue";
export default {
name: 'BodyMain',
components: {
InputData
},
data() {
return {
nm: 'test',
}
}
}
</script>
触发自定义事件,父组件中通过 v-model 绑定的值发生改变,实现子组件向父组件传值
<!-- 子组件 -->
<template>
<div>
<span>{{title}}:</span>
<input type="text" :name="name" :placeholder="placeholder" :value="value || ''" @keyup="change" />
</div>
</template>
<script>
export default {
name: 'InputData',
props: {
title: {
type: String,
required: true,
default: ''
},
name: {
type: String,
required: true,
default: ''
},
placeholder: {
type: String,
required: false,
default: ''
},
value: {
type: String,
required: false
}
},
emits: ['update:value'], //在emits中声明自定义事件,事件名称中必须有一个固定前缀"update:",要更新哪个属性,就把属性名写到update后面
methods: {
change(e) {
this.$emit('update:value', e.target.value) //将value绑定的值改为input输入的值,由于父组件中通过v-model绑定value,所以值也会实时变化
}
}
}
</script>
上一篇:Vue入门必备知识
下一篇:Vue插槽slot使用方法