Vue3 + ts 子组件 emit 方法在父组件中未触发,如何解决?

vue3.0+tsx中子组件注册的两个emit方法,点击事件btnclick在父组件中正常,然后一个接收消息的事件rollback,没触发。

子组件:

<button @click="handleEmit">点击</button>
...
const emit = defineEmits(['receivemsg', 'btnclick']);
...
//正常触发
const handleEmit = () => {
  emit('btnclick', '111')
};
//接收socket消息
const onMessage = (msgEvent: any) => {
    //收到服务器信息,心跳重置并发送
    startHeartbeat();
    const msg = msgEvent.data;

    if (msg.indexOf('pong') > 0) {
        return;
    }
    ElNotification.warning({
        title: '消息提醒',
        dangerouslyUseHTMLString: true,
        message: `您有一条来自${JSON.parse(msg).data.name}的新消息,请及时处理`,
        offset: 60,
    });
    emit('receivemsg', msg);
};

接收socket消息是正常的,msg也能正常打印,就是父组件无法接收事件
父组件:

<global-websocket uri="/admin/ws/info" ref="socketComponent" @receivemsg="receivemsg" @btnclick="handleBtnEmit" />
...

// 引入组件
const GlobalWebsocket = defineAsyncComponent(() => import('/@/components/Websocket/index.vue'));
const socketComponent = ref();
//子组件的emit方法 这个正常
const handleBtnEmit = (data: any) => {
    console.log(111, data);
};
//这个没打印
const receivemsg = (data: any) => {
    console.log(111, data);
};

父组件没触发rollback方法,有没有大神帮忙看下怎么回事

阅读 3.3k
avatarAI BotBETA

在 Vue 3 中使用 TypeScript (tsx.vue 文件中的 <script lang="ts">),子组件通过 defineEmits 定义的 emit 事件在父组件中未触发通常有以下几种可能的原因:

  1. 异步组件的加载:你正在使用 defineAsyncComponent 来异步加载子组件。如果子组件在父组件尝试监听事件之前还没有加载完成,那么父组件是无法接收到事件的。
  2. 事件名拼写错误:检查子组件中 emit 调用的事件名 'btnclick' 是否与父组件中监听的事件名完全一致(包括大小写)。
  3. 子组件内部问题:确保 handleEmit 方法确实被调用了,并且 emit 函数是可用的(没有被覆盖或改变)。
  4. Vue 开发者工具:使用 Vue 开发者工具来检查事件是否从子组件正确发出。
  5. Vue 3 的 Composition API:如果你正在使用 Composition API,确保 setup 函数中的 emit 是通过 defineEmits 正确定义的。

基于你提供的代码,以下是一些可能的解决步骤:

确保异步组件加载完成

你可以在父组件中添加一个加载完成的检查或等待机制。例如,你可以使用 onMounted 钩子来确保子组件已经加载完成。

检查事件名

确保事件名在子组件和父组件中完全一致。

检查子组件的 setup 函数

确保 setup 函数中没有覆盖或改变 emit 变量。

完整示例

如果上述检查都没问题,那么这里有一个完整的示例,包括子组件和父组件的代码,它们应该可以正常工作:

子组件 (GlobalWebsocket.vue)

<template>
  <button @click="handleEmit">点击</button>
</template>

<script lang="ts">
import { defineComponent, defineEmits } from 'vue';

export default defineComponent({
  setup() {
    const emit = defineEmits(['rollback', 'btnclick']);

    const handleEmit = () => {
      emit('btnclick', '111');
    };

    return {
      handleEmit
    };
  }
});
</script>

父组件

<template>
  <global-websocket uri="/admin/ws/info" @rollback="rollback" @btnclick="handleBtnEmit" />
</template>

<script lang="ts">
import { defineComponent, ref, defineAsyncComponent } from 'vue';
import GlobalWebsocket from '/@/components/Websocket/index.vue'; // 确保路径正确

const GlobalWebsocketAsync = defineAsyncComponent(() => import('/@/components/Websocket/index.vue'));

export default defineComponent({
  components: {
    GlobalWebsocket: GlobalWebsocketAsync // 使用异步组件
  },
  setup() {
    const handleBtnEmit = (data: any) => {
      console.log(111, data);
    };

    return {
      handleBtnEmit
    };
  },
  methods: {
    rollback() {
      // 处理 rollback 事件
    }
  }
});
</script>

请注意,我在父组件中直接使用了 GlobalWebsocketAsync 作为组件名,而不是通过 ref。如果你需要使用 ref 来访问子组件的实例,确保你只在需要的时候(例如在某个方法中)使用它,而不是在模板中。在模板中直接使用 ref 的值作为组件是不正确的。

2 个回答

没问题啊,OP检查一下自定义事件的拼写问题?

Vue SFC Playground Demo

<!-- App.vue -->
<script setup>
import Comp from './Comp.vue'

const handleBtnEmit = (data) => {
    console.log("App.handleBtnEmit", data);
};

const handleRollback = (data) => {
    console.log("App.handleRollback", data);
};
</script>

<template>
  <Comp @rollback="handleRollback" @btnclick="handleBtnEmit"/>
</template>
<!-- Comp.vue -->
<script setup>
const emit = defineEmits(['rollback', 'btnclick']);

const handleBtnclick = () => {
  emit('btnclick', new Date().valueOf())
};

const handleRollback = () => {
  emit('rollback', new Date().valueOf())
}

setInterval(() => {
  handleRollback()
}, 1 * 1000)

</script>

<template>
   <button @click="handleBtnclick">点击</button>
</template>

image.png
你的socket方法中emit传给父组件的名称是receivemsg,但是用的是rollback

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题