从一个vue-next repo的issues搞懂$attrs和emits选项关系

336 阅读1分钟

issues问题🔗如下:

github.com/vuejs/vue-n…

复现流程:

Parent:

<template>
  <div>
    <Mid @myEvent="myEventHandler" />
  </div>
</template>

<script>
import Mid from "./Mid.vue";

export default {
  components: { Mid },
  methods: {
    myEventHandler() {
      console.log("triggered");
    },
  },
};
</script>

Mid组件:

<template>
  <div>
    <Child v-bind="$attrs" />
  </div>
</template>

<script>
import Child from "./Child.vue";

export default {
  emits: ["myEvent"],
  inheritAttrs: false,
  components: { Child },
  mounted(){
      console.log("Mid: ",this.$attrs)
  }
};
</script>

Child组件:

<template>
  <button @click="$emit('myEvent')">Emit Child</button>
</template>

<script>
export default {
  mounted() {
    console.log("Child: ", this.$attrs);
  },
};
</script>

操作点击Child组件的Emit Child按钮,并没有触发Parent的myEventHandler事件

PS: 其实这个issues并不是真正的bug,却引发了讨论

首先咱们就来看看为什么,首先从Parent入手:

1.Parent要做的只有一件事,引入Mid组件,并将自身的myEventHandler方法传给Mid,传值方式为:@myEvent

2.Mid组件用emits接收了myEvent事件,并在mounted挂载阶段输出了this.$attrs

3.Child组件mounted挂载阶段输出了this.$attrs,并且在按钮的点击事件中调用了emit定义的myEvent

最后的结果为:

Child:  Proxy {__vInternal: 1}
Mid:  Proxy {__vInternal: 1}

并没有从$attrs中获取到myEvent,最后的myEvent也没有调用成功

emit传递数据到Mid组件后并没有继续传递到Child,而是把attrs传到Child组件上,而从刚才输出的MId可以发现,并没有输出emit传递的myEvent,可以得知,attrs并不会传递给定义给组件的emit选项,我们试着将Mid修改一下,去掉emits: ["myEvent"],可得到如下输出:

Child:  Proxy {__vInternal: 1, onMyEvent: ƒ}
Mid:  Proxy {__vInternal: 1, onMyEvent: ƒ}

由此得知,$attrs并不会传递emits选项中的事件,这引发了一个关于vue-next框架的讨论,有兴趣可以看看

vuejs/rfcs#397 (comment)