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

Vue3组合式API模态框开关异常及ESC按键监听需求

解决Vue3组合式API模态框的开关问题(含ESC关闭支持)

我来帮你一步步排查并解决这些问题,咱们先从根源找起:

一、修复v-if无法打开模态框的问题

你的代码里有两个关键的模板错误导致了这个问题:

  1. 多余的@click绑定:你在<announcement-modal>标签上加了@click="isModalOpen = true",这不仅没必要,还可能干扰模态框的状态切换;
  2. 标签闭合错误:App.vue的模板开头只有一个<div>,结尾却多写了两个</div>,这会破坏Vue的模板解析逻辑,导致组件渲染异常。

修复后的App.vue模板部分:

<template>
  <div class="min-h-screen flex items-center justify-center">
    <button @click="isModalOpen = true" type="button" class="btn btn-blue">Open Modal</button>
    <announcement-modal v-if="isModalOpen" v-model:isOpen="isModalOpen"></announcement-modal>
  </div>
</template>

另外注意:你导入的组件是AnnouncementModal,但子组件文件名是Announcement.vue,这大概率是笔误,建议把子组件文件名改成AnnouncementModal.vue,保持命名一致避免混淆。

二、修复v-show下关闭按钮失效的问题

关闭按钮失效的核心原因是事件冒泡:你之前在模态框组件上绑定的@click="isModalOpen = true",会在点击关闭按钮时触发冒泡事件,把isModalOpen重新设为true,直接抵消了关闭操作。

解决方法就是去掉组件上多余的@click绑定,同时修正子组件的模板闭合错误(你的子组件多写了两个</div>)。

三、添加ESC按键关闭功能及卸载时移除监听

我们可以在子组件的setup中添加全局键盘监听,捕获ESC键触发关闭逻辑,同时在组件卸载时移除监听,避免内存泄漏。

修改后的子组件(AnnouncementModal.vue)完整代码:

<template>
  <!-- 添加遮罩层,点击遮罩也能关闭模态框 -->
  <div class="modal-overlay" @click="closeModal">
    <div class="modal-content text-center" @click.stop>
      <button @click="closeModal" type="button" class="btn btn-blue">
        Dismiss
      </button>
    </div>
  </div>
</template>
<script>
import { onUnmounted } from "vue"
export default {
  props: ["isOpen"],
  // 显式声明emits,符合Vue3最佳实践,避免警告
  emits: ["update:isOpen"],
  setup(props, context) {
    // 监听ESC按键
    const handleEscKey = (e) => {
      if (e.key === 'Escape' || e.keyCode === 27) {
        closeModal()
      }
    }

    // 组件挂载后添加全局监听
    window.addEventListener('keydown', handleEscKey)

    // 组件卸载时移除监听,防止内存泄漏
    onUnmounted(() => {
      console.log("after unmounted")
      window.removeEventListener('keydown', handleEscKey)
    });

    const closeModal = () => {
      // 触发v-model的更新事件,注意驼峰命名对应
      context.emit("update:isOpen", false);
    }

    return { closeModal }
  }
}
</script>
<style scoped>
/* 给模态框添加基础样式,让它更符合视觉预期 */
.modal-overlay {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background-color: rgba(0, 0, 0, 0.5);
  display: flex;
  align-items: center;
  justify-content: center;
}

.modal-content {
  background-color: white;
  padding: 2rem;
  border-radius: 8px;
}
</style>

这里的@click.stop是为了阻止模态框内容区域的点击事件冒泡到遮罩层,避免点击按钮或内容时意外关闭模态框。

最终完整的App.vue代码

<template>
  <div class="min-h-screen flex items-center justify-center">
    <button @click="isModalOpen = true" type="button" class="btn btn-blue">Open Modal</button>
    <announcement-modal v-if="isModalOpen" v-model:isOpen="isModalOpen"></announcement-modal>
  </div>
</template>
<script>
import { ref } from "vue";
import AnnouncementModal from "./components/AnnouncementModal";
export default {
  components: {
    AnnouncementModal,
  },
  setup() {
    const isModalOpen = ref(false);
    return {
      isModalOpen,
    }
  },
}
</script>

现在你的模态框应该可以正常开关,按下ESC键也能关闭,组件卸载时也会自动移除键盘监听啦。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.11 07:26:38