在 Vue 2.X 中,$dispatch 和 $broadcast 方法已经被废弃。官方认为基于组件树结构的事件流方式难以理解,并且在组件结构扩展时容易变得脆弱。因此,Vue 2.X 推荐使用其他方式来实现组件间的通信,例如通过 $emit 和 $on 方法,或者使用事件总线(Event Bus)
子组件向上派发事件 ,然后父组件会收到来自子组件发来的信息
<div id="app">
<div>
<my-fade></my-fade>
</div>
</div>
<script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.13/vue.js"></script>
<script>
//子组件
const ChildComponent={
template:`<div><h4>子组件</h4><button @click="dispatchEvent">派发事件</button></div>`, //模板,创建一个点击事件引用模式中的方法dispatchEvent
methods:{
//事件函数
dispatchEvent(){
//设置或叫创建自定义事件 custom-event,第2个是传递自定义事件的参数,传递给handleCustomEvent(message)
this.$emit('custom-event','Hello my vue,this is from child');
}
}
};
const ParentComponent={
//在模板中将子组件嵌入其中,并创建自定义custom-event事件绑定函数handleCustomEvent
template:`<div>
<h3>父组件</h3>
<p>接收到消息:{{message}}</p>
<child-component @custom-event="handleCustomEvent"></child-component>
</div>`,
data(){
return {
message:''
};
},
methods:{
//这里的参数message就是自定义事件中custom-event的参数
handleCustomEvent(message){
this.message=message;
console.log(this.message);
}
},
components:{
// 在父组件中注册子组件
'child-component':ChildComponent
}
};
const app=new Vue({
el:"#app",
components:{
'my-fade':ParentComponent
}
});
</script>
以下是派发事件,及父组件广播给所有子组件
<div id="app">
<parent-component></parent-component>
</div>
<script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.13/vue.js"></script>
<script>
//设置子组件
const ChildComponent={
template:`
<div>
<h3>子组件</h3>
<p>接收到消息:{{message}}</p>
</div>
`,
data(){
return {
message:''
};
},
methods:{
//主要作为是触发自定义事件时设置message的值
handleBroadcast(message)
{
this.message=message;
}
},
mounted(){
//$on主要用途即为监听自定义事件
this.$on('broadcast-event',this.handleBroadcast);
},
//在组件消毁前移除监听
beforeDestroy(){
this.$off('broadcast-event',this.handleBroadcast);
}
};
//设置父组件
const ParentComponent={
template:`<div>
<h3>父组件</h3>
<button @click="broadcastEvent">广播事件</button>
<child-component></child-component> //嵌入子组件
</div>
`,
components:{
//在父组件中注册子组件
'child-component':ChildComponent
},
methods:{
broadcastEvent(){
this.$children.forEach((child)=>{
if(child.handleBroadcast)
{
//设置自定义事件broadcast-event,并传递hello from Parent这个参数
child.$emit('broadcast-event','Hello from Parent')
}
})
}
}
};
const app=new Vue({
el:"#app",
components:{
'parent-component':ParentComponent
}
})
</script>
广播事件的典型使用场景
- 表单验证
场景描述:在复杂的表单中,可能有多个子组件负责不同的表单字段验证。父组件可以通过广播事件通知所有子组件进行验证操作。
实现方式:父组件触发一个广播事件(如 validate),所有子组件监听该事件并执行各自的验证逻辑。
- 更新状态
场景描述:父组件需要更新多个子组件的状态,例如在购物车页面中,父组件需要通知所有子组件更新商品数量或价格。
实现方式:父组件广播一个事件(如 update-cart),子组件监听该事件并根据传递的数据更新自身状态。
- 动态数据同步
场景描述:在多级嵌套的组件结构中,父组件需要将动态数据同步到多个子组件中。
实现方式:父组件通过广播事件将数据传递给所有子组件,子组件接收数据并更新视图。
- UI 状态更新
场景描述:父组件需要统一更新多个子组件的 UI 状态,例如在多级菜单中,父组件需要通知所有子菜单项更新显示状态。
实现方式:父组件广播一个事件(如 toggle-menu),子组件监听该事件并根据传递的参数更新显示状态。
- 数据刷新
场景描述:在数据列表中,父组件需要通知所有子组件刷新数据,例如在用户管理页面中,父组件需要通知所有子组件重新加载用户数据。
实现方式:父组件广播一个事件(如 refresh-data),子组件监听该事件并调用数据加载方法。
- 示例:表单验证场景
假设有一个表单,包含多个子组件,每个子组件负责一个表单字段的验证。父组件需要在提交表单时触发所有子组件的验证逻辑。
<div id="app">
<form-component></form-component>
</div>
<script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.13/vue.js"></script>
<script>
const FieldComponent={
//从父组件中获取到field,pwd,pwd2他们的值
//@blur失去焦点时触发
props:['field','pwd','pwd2'],
template:`<div>
<label :for="field.id">{{field.label}}</label>
<input :id="field.id" v-model="field.value" :type="field.type" :name="field.id" @blur="validateField" autocomplete="false">
<span v-if="field.error" style="color:red;">{{field.error}}</span>
</div>`,
methods:{
//验证函数
validateField(){
if(!this.field.value)
{
this.field.error='此字段必填';
}else
{
switch(this.field.id)
{
case 'username':
if(this.field.value.length<4 || this.field.value>21)
{
this.field.error='用户名字符在4-21之间';
}else
{
this.field.error='';
}
break;
case 'pwd':
let regex=/[A-Z]/g;
if(this.field.value.length<6 || !regex.test(this.field.value))
{
this.field.error='密码必须大于6个字符,并包含大写字母';
}else
{
this.field.error='';
console.log(this.field.value);
//设置自定义更新事件,即pwd更新时触发
this.$emit('update:pwd',this.field.value);
}
break;
case 'pwd2':
if(this.field.value != this.pwd)
{
this.field.error='两次密码不一致';
}else
{
this.field.error='';
}
break;
case 'email':
if(!this.field.value.includes('@'))
{
this.field.error='邮箱不正确';
}else
{
this.field.error='';
}
break;
}
}
}
},
mounted(){
//监听自定义validate事件,并触发验证函数
this.$on('validate',this.validateField);
},
//在消毁前注销掉事件
beforDestroy(){
this.$off('validate',this.validateField);
}
};
const FormComponent={
//父组件模板,嵌入子组件<field-component 并循环fields数据
//@update:pwd即自定义更新事件,getPwd为函数用于设置pwd更新后的值,也可以直接写成@update:pwd="pwd=$event"
template:`
<form @submit.prevent="submitForm">
<field-component v-for="field in fields" :key="field.id" :field="field" :pwd="pwd" :pwd2="pwd2" @update:pwd="getPwd"></field-component>
<button type='submit'>提交</button>
</form>
`,
components:{
//注册字段子组伯
'field-component':FieldComponent
} ,
data(){
return{
fields:[
{id:'username',label:'用户:',type:'text',value:'',error:''},
{id:'pwd',label:'密码:',type:'password',value:'',error:''},
{id:'pwd2',label:'重复:',type:'password',value:'',error:''},
{id:'email',label:'邮箱:',type:'email',value:'',error:''}
],
pwd:'', //pwd,pwd2主要作用是为了在验证他们是否相等时使用
pwd2:''
}
},
methods:{
//在提交时触发的函数
submitForm(){
this.$children.forEach((child)=>{
if(child.validateField)
{
//在子组件中触发自定义的验证函数
child.$emit('validate');
}
});
//如果在有一个field.error为真,即会大子组件字段中显示
const hasErrors=this.fields.some((field)=>field.error);
if(!hasErrors)
{
alert('表单提交成功');
}
},
getPwd(val){
this.pwd=val;
}
}
}
const app=new Vue({
el:"#app",
components:{
'form-component':FormComponent
}
});
</script>
- @update:pwd:
@ 是 Vue 中用于监听事件的简写符号。
update:pwd 是一个自定义事件名称。update 是一个常见的前缀,用于表示数据更新的事件,而 pwd 是具体的字段名。
- pwd = $event:
pwd 是父组件 FormComponent 中定义的数据属性。
e
v
e
n
t
是
V
u
e
中的一个特殊变量,表示事件触发时传递的参数。在这里,
event 是 Vue 中的一个特殊变量,表示事件触发时传递的参数。在这里,
event是Vue中的一个特殊变量,表示事件触发时传递的参数。在这里,event 是子组件 FieldComponent 通过 $emit(‘update:pwd’, this.field.value) 发出的值。
pwd = $event 是一个表达式,表示将子组件发出的值赋值给父组件的 pwd 属性。