You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Vue3父子组件emit事件不触发 模态框二次打开失败问题

问题根因

一共三个核心问题导致事件不触发、模态框二次打开失效:

  1. 父组件Bets.vue存在语法结构错误:
    • setup()函数被提前闭合,showBetModal状态和closeBetModal方法的定义位置不在组件配置范围内,模板绑定的方法/状态实际没有正确挂载
    • 模板标签嵌套错误,n-card标签提前闭合,末尾多写了一个无效的</n-card>闭合标签
  2. 子组件bet-modal.vue的关闭逻辑错误:
    • 底部Cancel、Add按钮的点击事件仅修改了子组件内部维护的本地show变量,没有调用closeBetModal方法向外抛出关闭事件。这就导致点击按钮时模态框视觉上消失,但父组件的showBetModal状态始终停留在第一次打开后的true。第二次点击打开按钮时,给已经是true的状态重复赋值true,Vue不会触发组件更新,模态框自然没有响应。
    • 子组件内部额外维护了一份和父组件prop脱节的show状态,即使加了watch同步,也很容易出现父子状态不同步的隐性问题。
  3. 父组件事件绑定写法不规范:@closeBetModal="closeBetModal()"会在模板渲染时就立即执行函数,正确写法应该是直接绑定函数引用@closeBetModal="closeBetModal"。
修复方案

1. 修复父组件Bets.vue

修正标签嵌套和setup语法结构,去掉多余的闭合符号,规范事件绑定写法:

<template>
  <div>
    <n-card title="Bets" id="bets-table-card">
      <template #header-extra>
        <n-icon class="pointer" @click="showBetModal = true">
          <add-circle-outline></add-circle-outline>
        </n-icon>
      </template>
      <!-- 把bet-modal放到正确的嵌套位置,修正事件绑定写法 -->
      <bet-modal :showModal="showBetModal" @closeBetModal="closeBetModal"/>
    </n-card>
  </div>
</template>

<script lang="ts">
import { defineComponent, ref } from "vue";
import {
  NCard,
  NIcon,
} from "naive-ui";
import { AddCircleOutline } from "@vicons/ionicons5";
import BetModal from "@/components/modals/bet-modal.vue";

export default defineComponent({
  name: "Bets",
  components: {
    NCard,
    AddCircleOutline,
    NIcon,
    BetModal
  },
  setup() {
    // 所有状态和方法都放到setup内部定义
    const showBetModal = ref(false);
    const closeBetModal = () => {
      showBetModal.value = false;
    };

    return {
      showBetModal,
      closeBetModal
    };
  },
});
</script>

注:已移除和当前问题无关的导入、变量定义,可将之前删掉的表格相关逻辑补回setup内即可

2. 修复子组件bet-modal.vue

去掉冗余的本地show状态,直接绑定prop传入的显示状态,所有关闭操作都触发emit通知父组件更新状态,避免父子状态不同步:

<template>
   <n-modal :show="showModal" @close="handleClose">
        <n-card title="Add Bets">
            <template #footer>
                <!-- 所有关闭操作统一调用关闭方法,向外抛事件 -->
                <n-button @click="handleClose">Cancel</n-button>
                <n-button @click="handleSubmit">Add</n-button>
            </template>
        </n-card>
    </n-modal>
</template>

<script>
import { defineComponent } from "vue";
import { NModal, NButton, NCard } from "naive-ui";
export default defineComponent({
    name: "BetModal",
    components: {
        NModal,
        NButton,
        NCard
    },
    props: {
        showModal: {
            type: Boolean,
            required: true
        },
    },
    emits: ["closeBetModal"],
    setup(props, { emit }) {
        const handleClose = () => {
            // 关闭时统一向外抛事件,由父组件修改状态
            emit("closeBetModal");
        };

        const handleSubmit = () => {
            // 这里写新增投注记录的提交逻辑
            // 提交完成后再关闭模态框
            emit("closeBetModal");
        };

        return {
            handleClose,
            handleSubmit
        }
    }
})
</script>
修复说明
  • 遵循Vue单向数据流原则:模态框的显示状态完全由父组件维护,子组件不私自修改prop相关的状态,所有状态变更都通过事件通知父组件修改,从根源上避免状态不一致
  • 修正后模态框关闭时会正确触发事件,父组件的showBetModal会被设置为false,下次点击打开按钮时状态从false变为true,模态框可以正常弹出
  • 去掉了子组件冗余的watch和本地ref,代码逻辑更简洁,不会出现状态不同步的隐性bug

内容的提问来源于stack exchange,提问作者Jake Steele

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 22:45:44