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

