本文已参与「新人创作礼」活动, 一起开启掘金创作之路。
$listeners
$listeners是vue的一个实例属性,它用于获取父组件传过来的所有事件函数
<!-- 父组件 -->
<Child @event1="handleEvent1" @event2="handleEvent2" />
// 子组件
this.$listeners; // { event1: handleEvent1, event2: handleEvent2 }
$emit和$listeners通信的异同
相同点:均可实现子组件向父组件传递消息
差异点:
$emit更加符合单向数据流(父组件的数据流向子组件),子组件仅发出通知,由父组件监听做出改变;而$listeners则是在子组件中直接使用了父组件的方法(没有通知的过程)。- 调试工具可以监听到子组件
$emit的事件,但无法监听到$listeners中的方法调用。(想想为什么)- 由于
$listeners中可以获得传递过来的方法,因此调用方法可以得到其返回值。但$emit仅仅是向父组件发出通知,无法知晓父组件处理的结果
案例场景分析 1(组件通信):
子组件发生了一件事情,需要等待父组件处理,并且在父组件处理过后,子组件还要根据父组件传递的数据再做处理
这里的这个子组件,需求是这里的数据isLoading再点击过后会切换为true,从而达到这个按钮组件无法重复点击的目的; 问题就在于,这里的数据error是需要经过父组件传入的,并且这个error的数据是异步的,所以就是需要解决这样一个场景
第一种解决办法
$emit支持传入多个参数,所以让子组件,向父组件传递一个回调函数,由父组件代替子组件调用。
$emit是通知父组件,通知过后就结束了,是拿不到父组件的返回结果的
子组件:
methods: {
handleClick() {
/**
*count随着点击数+1
错误消息清空
为了防止重复点击需要先将isLoading设置为true
通知父组件:“我被点击了”,并且向父组件传递当前的点击次数
等待父组件处理(可能是异步的),随后通过父组件拿到error的值
*/
this.count++;
this.isLoading = true;
this.error = "";
this.$emit("dot", this.count, (err) => {
// 箭头函数,不用担心父组件调用时候的this指向
this.isLoading = false;
this.error = err;
});
},
},
父组件:
handleDot(count, callback) {
setTimeout(() => {
// 处理完成
callback("格式错误");
}, 300);
},
第二种解决办法
$listeners是 vue 提供的一个实例属性,利用$listeners获取父组件传递的事件函数,实现在子组件调用,从而达到目的
子组件:
methods: {
async handleClick() {
/**
*count随着点击数+1
错误消息清空
为了防止重复点击需要先将isLoading设置为true
通知父组件:“我被点击了”,并且向父组件传递当前的点击次数
等待父组件处理(可能是异步的),随后通过父组件拿到error的值
*/
this.count++;
this.isLoading = true;
this.error = "";
if (this.$listeners.click) {
// 判断父组件是否传递了事件处理函数click
const result = await this.$listeners.click(this.count);
this.isLoading = false;
this.error = result;
console.log(result);
}
},
},
父组件:
第三种解决方法
通过父组件向子组件传递属性的方式,和第二种方式有点像
子组件:
methods: {
async handleClick() {
/**
*count随着点击数+1
错误消息清空
为了防止重复点击需要先将isLoading设置为true
通知父组件:“我被点击了”,并且向父组件传递当前的点击次数
等待父组件处理(可能是异步的),随后通过父组件拿到error的值
*/
this.count++;
this.isLoading = true;
this.error = "";
if (this.click) {
const result = await this.click(this.count);
this.isLoading = false;
this.error = result;
}
},
},
父组件: