【问题标题】:Error in v-on handler: "TypeError: Cannot read property 'emit' of nullv-on 处理程序中的错误:“TypeError:无法读取属性‘emit’ of null
【发布时间】:2020-09-04 16:20:55
【问题描述】:

我正在开发一个 vue 项目,并希望在一段时间后从子组件向父组件发出事件(用户将鼠标悬停在元素上,几秒钟后,应该会发出)。

我很确定我在其他组件中已经完成了与此类似的发射,并且它以前也有效,但现在我得到了这两个错误。我不确定发生了什么变化。我在第一次尝试后实现了 vuex,回到了这个,现在我不确定发生了什么?但也许我只是删除了一些东西,或者我不知道。 我还在似乎有问题的行上方尝试了一个控制台日志,并且该值似乎不为空。 这些是我得到的错误:

[Vue 警告]:v-on 处理程序中的错误:“TypeError:无法读取 null 的属性 'emit'” 之后出现错误 Cannot read property 'emit' of null 第一个错误提到,该错误是在组件中发现的,如下所示。

我看到的与此问题相关的大部分内容都是关于人们不正确地执行 es6 箭头功能,所以 this.timer = setInterval(() => this.countdown(), 1000); 可能有问题?我不太确定。

这是组件(请原谅凌乱的代码):

<template>
    <div
        :id="id"
        class="board"
        @dragenter.prevent
        @dragover.prevent
        @drop.prevent="drop"
        @dragenter="dragenter($event)"
        @dragover="dragover($event)"
        @dragleave="dragleave($event)"
    >
        <slot class="row"/>
        <div
        :id="`ui-details-${id}`"
        v-show="extendedHover">
        Long hover
        </div>
    </div>
</template>

<script>
export default {
  name: 'Devices',
  props: ['id', 'acceptsDrop'],
  data() {
    return {
      extendedHover: false,
      timer: null,
      totalTime: 2,
    };
  },
  methods: {
    drop(e) {
      if (e.dataTransfer.getData('type') === 'layout') { // only accept dropped cards, not boards
        const layoutId = e.dataTransfer.getData('layout_id');
        this.$emit('dropped-layout', layoutId);
      }
      clearInterval(this.timer);
      this.timer = null;
      this.totalTime = 2;
      console.log(e.dataTransfer.getData('card_id'));
      e.target.classList.remove('hover-drag-over');
      this.extendedHover = false;
      // this.$emit('cancel-hover');
      console.log('-------------dropped');
      console.log(e);
      console.log(e.dataTransfer.getData('type'));
      /* const cardId = e.dataTransfer.getData('card_id');
      console.warn('dropped onto device');
      this.$emit('dropped-component', cardId);
      e.target.classList.remove('hover-drag-over'); */
    },
    dragenter(e) {
      // on dragenter we start a countdown of 1s
      // over this value --> we see it as a long hover
      console.log('------------dragenter');
      console.log(e);
      this.timer = setInterval(() => this.countdown(), 1000);
    },
    dragover(e) {
      if (this.acceptsDrop) {
        e.target.classList.add('hover-drag-over');
      }
    },
    dragleave(e) {
      if (this.acceptsDrop) {
        clearInterval(this.timer);
        this.timer = null;
        this.totalTime = 2;
        e.target.classList.remove('hover-drag-over');
        this.extendedHover = false;
        // this.$emit('cancel-hover');
      }
    },
    countdown() {
      this.totalTime -= 1;
      if (this.totalTime === 0) {
        this.extendedHover = true;
        console.warn(this);
        this.$emit('long-hover'); //this is the problematic line
      }
    },
  },
};
</script>

<style scoped>
    .board{
        width: 100%;
        min-height: 200px;
    }
</style>

感谢您的帮助!

编辑: console.log 的输出如下VueComponent {_uid: 18, _isVue: true, $options: {…}, _renderProxy: Proxy, _self: VueComponent, …},如果需要,还可以扩展以获取更多信息。

Edit2:这个组件是根据父组件中的数组生成的,就像这样。所以这个槽现在只被一个字符串填充。

<div
                class = "col-4"
                v-for="(device, index) in devices"
                v-bind:key="`device-${device.ip}`"
            >
                <Devices
                :id="`board-${device.ip}`"
                v-bind:class="{'droparea-device':true, 'your-device': (index === thisDeviceIndex)}"
                @dropped-layout="$emit('dropped-layout', $event, index)"
                @long-hover="fetchAvailableUIsForDevice(device.device_name)"
                @cancel-hover="cancelHover()"
                :acceptsDrop=true
                >
                {{device.device_name}}
                </Devices>
            </div>

编辑3: 对于测试,我也只是想尝试一个非常基本的东西。所以我添加了一个带有这样的点击事件的按钮(但仍然存在错误):

 <b-button
        type="button"
        variant="success"
        v-on:click="$emit('long-hover')"
        >
          Control Center
        </b-button>

【问题讨论】:

  • console.warn(this); 的输出是什么?
  • @Anatoly,我将输出添加到问题描述的底部。这样做有帮助吗?
  • 直接在dragenter中调用this.$emit('long-hover');,问题会消失吗?
  • 我用这一行测试了它 this.timer = setInterval(() => this.$emit('long-hover'), 1000); 和同样的错误遗迹。此外,我尝试完全摆脱计时器并仅使用 this.$emit('long-hover') 对其进行测试,并且错误也仍然存在。我觉得有点奇怪......
  • 你确定那个 $emit 调用中的错误而不是另一个错误吗?

标签: javascript vue.js events components emit


【解决方案1】:

我认为是的原因可能来自您建议的路线。 this.countdown 指向无处,因为这里的 this 指针不属于 Vue

this.timer = setInterval(() => this.countdown(), 1000);

要修复,建议你试试

dragenter(e) {
      // on dragenter we start a countdown of 1s
      // over this value --> we see it as a long hover
      console.log('------------dragenter');
      console.log(e);
      let vm = this;  // assign Vue this to vm
      this.timer = setInterval(() => vm.countdown(), 1000); // use vm instead of this
    },

【讨论】:

  • 不幸的是,如果我这样尝试,我仍然会出现同样的错误,您还有其他想法吗?
  • 我也尝试了一些非常基本的方法,比如像在其他组件中那样在按钮上添加发射。我将它添加到我的问题描述中。
【解决方案2】:

感谢您对这种奇怪行为的帮助。在用一个按钮测试了发射后,它仍然不起作用,我认为这是非常可疑的。 只是为了确保,我查看了父组件,因为当子组件发出事件时我执行了一个函数。 当我查看函数时,我有以下行:

this.socket.emit('fetch_available_uis', { device_name: device });

这里是错误描述!因为正如我所说,我包含了 Vuex,之后它就不再工作了。添加 vuex 时,我还将套接字连接移到了 store,所以现在确实没有 this.socket 或者更好地说是上面提到的 null 值。 我修复了这一行,现在错误消失了!

因此,对于以后遇到类似问题的其他人,我建议查看接收您孩子事件的父母中的功能并检查那里的错误。因为即使错误在父级中,错误消息仍然显示在子级中发现错误可能是因为它将发出事件的组件作为源而不是上面的组件。

【讨论】: