父子组件之间的通信,最主流的方法就是前面章节讲解的 props
和
$emit()
方法。父组件通过自定义属性向子组件传递数据,子组件使用 props
选项接收父组件传递过来的数据;父组件通过绑定自定义事件接收子组件传递过来的数据,子组件通过 this.$emit()
方法调用父组件的自定义事件,来向父组件传递数据。
本节针对组件的自定义事件,为读者讲解 Vue.js 3.x 框架中有关组件自定义事件的规定和语法要求。
1、定义自定义事件
首先,组件中的自定义事件名称和自定义属性名称都有自动大小写转换的功能。父组件中绑定的是短横线命名的事件名,则在子组件中触发该事件时应该采用驼峰式命名。
例如:父组件定义了一个名为 custom-event 的自定义事件,代码如下所示。
<custom-com @custom-event="customEvent"></custom-com>
则子组件在触发该事件时应该如下为如下格式。
this.$emit('customEvent');
其次,Vue
框架建议在要触发自定义事件的组件中,最好使用
emits
选项将自定义组件定义一下,这就和组件使用 props
选项接收自定义属性的性质是一样的,这样可以更好地记录组件是如何工作的。
例如:custom-com
组件绑定了
custom-event
自定义事件,应该在该组件的定义过程中使用
emits
选项定义好自定义事件 custom-event
,代码如下所示。
app.component('custom-com',{
emits:[ 'customEvent']
})
2、验证抛出的事件
组件的 emits 选项不仅可以定义自定义事件,还可以对自定义事件进行验证。对事件进行验证的目的是指示事件在某个条件下是否有效。若需要对自定义事件进行验证,则 emits 选项的取值必须是一个对象。同时对象成员名为自定义事件名,对象成员值为一个函数,当该函数返回 true时指示自定义事件是有效的,当该函数返回 false 时指示自定义事件是无效的。
当下对事件的验证机制仅仅是无效时在控制台中返回一个警告,并不会真正的阻止事件的触发,这种情况下需要开发人员在判定事件无效时使用 Error
类抛出一个自定义错误。
【示例 4-8
】
验证抛出的事件
。制作一个输入手机号码和电子邮箱的组件,名为
my-subscribe
,在组件的 template
选项中,为输入手机号码和电子邮箱的文本框双向绑定变量。同时组件中的命令按钮单击时,触发父组件的自定义事件,并将用户输入的手机号码和电子邮箱地址传递给这个自定义事件。
组件需要对可能触发的父组件的自定义事件进行验证,当手机号码和电子邮箱地址都不为空时,自定义事件有效;否则无效。
HTML 代码如下所示。
<div id="app">
<my-subscribe @my-custom="subscribe"></my-subscribe>
</div>
Vue 代码中父组件代码如下所示。
let app=Vue.createApp({
methods:{
subscribe(obj){
console.log(obj)
}
}
})
Vue 代码中注册
my-subscribe
组件的代码如下所示。
app.component('my-subscribe',{
data:function(){
return {
phone:'', // 用于双向绑定手机号码文本框的变量
email:'' // 用于双向绑定电子邮箱文本框的变量
}
},
// 对父组件自定义事件 myCustom 的验证
emits:{
myCustom:({phone,email})=>{
if(phone!=="" && email!==""){
return true;
}else{
throw new Error('手机号码和电子邮箱不得为空');
return false;
}
}
},
template:`
<div class="form">
<div class="formItem">
手机号码:<input type="text" name="phone" v-model="phone" />
</div>
<div class="formItem">
电子邮箱:<input type="text" name="email" v-model="email" />
</div>
<div class="formItem">
<button @click="submit">提交订阅</button>
</div>
</div>
`,
methods:{
submit(){
this.$emit('myCustom',{phone:this.phone,email:this.email});
}
}
})
app.mount('#app')
在上述代码中,第 09
行至第第
18
行对父组件绑定的自定义事件
myCustom
进行了
emits
验证。当电话号码或电子邮箱为空时,第 14
行在
return false;
之前还抛出了一个
Error
错误,错误提示语为“手机号码和电子邮箱不得为空”,这样系统就不会继续触发 myCustom
事件了。
3、在组件上使用 v-model 指令
首先仔细想一想在表单元素上使用 v-model
指令实现双向绑定时的情景。代码如下所示。
<input type="text" v-model="phone" />
上述代码相当于使用 value
属性接收
phone
变量,同时在
input
事件中实现双向绑定,代码如下所示。
<input :value="phone" @input="phone=$event.target.value" />
要注意,event.target
在
js
部分使用可以直接书写为
event.target
,在
HTML
部分使用要书写为 $event.target。
v-model 指令不仅可以使用在表单元素上实现数据的双向绑定,也可以使用在具备表单元素的组件中,实现父子组件之间的双向传递。默认情况下,使用了 v-model
指令的父子组件之间的数据传递具备如下的规定。
- 父组件中 v-model 指令绑定的数据,子组件中要使用 modelValue 变量进行接收。
-
子组件的 input 事件要触发父组件的 update:modelValue 事件进行数据回传。
【示例 4-9
】
在组件中使用
v-model
指令
。注册一个全局组件,组件名为
my-age
,该组件用于用户设置自己的年龄。组件中具备一个 type=number
的微调器。在使用
my-age
组件的父组件中,具备一个命令按钮,该命令按钮要求能够接收到子组件中微调器的变化,并将最终的结果输出至控制台。
本示例的最终效果如图 4-8 所示。
![](https://i-blog.csdnimg.cn/direct/0360fc7bf41c41ef9c36cfde819c06d4.png)
HTML
代码如下所示。
<div id="app">
<h1>在组件中使用 v-model 指令</h1> <hr />
<my-age v-model="age"></my-age>
<button @click="submitAge">提交年龄</button>
</div>
在上述代码中,父组件为<my-age></my-age>
组件的使用双向绑定了
age
变量,这样
my-age
组件应该默认使用 modelValue
属性来接收该变量的值。同时默认绑定了
update:modelValue
事件。若不使用
v-model指令,则调用 my-age
组件的代码相当于如下所示的内容。
<my-age :model-value="age" @update:model-value="data=>age=data"></my-age>
或者也可以书写为:
<my-age :model-value="age" @update:model-value="age=$event"></my-age>
这里读者需要注意:Vue.js 3.0
框架允许在
HTML
代码中使用
$event
来访问子组件调取父组件自定义事件时传递过来的参数。
Vue 代码中注册
my-age
组件的代码如下所示。
app.component('my-age',{
props:['modelValue'],
emits:['update:modelValue'],
template:`
您的年龄:
<input type="number" min="18"
:value="modelValue"
@input="$emit('update:modelValue',$event.target.value)" />
`
})
app.mount('#app')
在上述代码中,第 02
行使用
props
来接收父组件传递过来的参数,第
03
行使用
emits
来定义父组件上绑定的自定义事件。第 08
行指出在录入年龄的文本框内容发生变化时调取父组件绑定的update:modelValue 事件,并将文本框自身的内容(及
$event.target.value
)作为参数传递给父组件。
Vue 实例代码如下所示。
let app=Vue.createApp({
data(){
return {
age:20
}
},
methods:{
submitAge(){
console.log(this.age);
}
}
})
通过本案例的制作能够说明:在带有文本框的组件中使用 v-model
指令可以快速的让子组件中的文本框与父组件进行通信,这本质上属于一种语法糖写法。该写法简化了父组件在调用子组件时的代码书写,同时又实现了父子组件之间的双向通信。
4、多个 v-model 指令的绑定
若某个组件中有多个表单元素,都需要和父组件之间实现双向通信,则可以在组件中使用多个 v-model指令,为每一个表单元素指定用于通信的变量和自定义事件。
通过上一节的讲解,我们知道,在组件中使用 v-model
指令,等同于用于父组件向子组件传递数据的变量名为 modelValue
,用于子组件向父组件传递数据的自定义事件名为
update:modelValue
。
若绑定多个 v-model
指令,则需要为每一个
v-model
指令指定传递数据的变量和回传数据的自定义事件名。我们可以通过在 v-model
指令后使用参数的方式来指定不同的变量名,则对应的自定义事件名也会有所不同。例如有如下所示的写法。
<my-component v-mode:first-data="fd"></my-component>
这意味着,父组件向子组件传递的数据使用 firstData
变量来进行传递,而子组件向父组件回传的数据使用名为 update:firstData
的自定义事件来实现。
【示例 4-10
】
在组件中使用多个
v-model
指令
。注册一个全局组件,组件名为
my-info
。该组件由两个文本框组成,一个用于输入用户昵称,另一个用于输入电子邮箱地址。页面中有一个命令按钮,单击该命令按钮在控制台中显示用户输入的内容。
本示例的最终效果如图
4-9
所示。
![](https://i-blog.csdnimg.cn/direct/3e95d5d1f889436b9110a5b8e0ee5803.png)
HTML
代码如下所示。
<div id="app">
<h1>在组件中使用多个 v-model 指令</h1> <hr />
<my-info v-model:user-nick="nick" v-model:user-email="email"></my-info>
<button @click="submitInfo">提交信息</button>
</div>
Vue
代码中注册
my-info
组件的代码如下所示。
app.component('my-info',{
props:['userNick','userEmail'],
emits:['update:userNick','update:userEmail'],
template:`
<div class="formItem">
用户昵称:<input type="text" :value="userNick"
@input="$emit('update:userNick',$event.target.value)" />
</div>
<div class="formItem">
用户邮箱:<input type="text" :value="userEmail"
@input="$emit('update:userEmail',$event.target.value)" />
</div>
`
})
app.mount('#app')
在上述代码中,第 02
行使用
props
选项接收了从父组件传递过来的两个属性,分别名为
userNick
和 userEmail。第
03
行使用
emits
选项定义了父组件绑定的两个自定义事件,分别名为
update:userNick
和 update:userEmail。
Vue
实例代码如下所示。
let app=Vue.createApp({
data(){
return {
nick:'您的昵称',
email:'您的电子邮箱'
}
},
methods:{
submitInfo(){
console.log(this.nick);
console.log(this.email);
}
}
})
通过上面的示例,我们可以这样理解:在父组件中使用 v-model
指令并携带参数,可以为子组件中的多个表单元素实现快捷的双向传递过程。