$listeners与$emit通信,简单案例分析

406 阅读2分钟

本文已参与「新人创作礼」活动, 一起开启掘金创作之路。

$listeners

$listenersvue的一个实例属性,它用于获取父组件传过来的所有事件函数

<!-- 父组件 -->
<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;
      }
    },
  },

父组件: