Vue.js 3中的DOM事件未转发给子组件

前端开发 2026-07-09

我正在把一个Vue2应用迁移到Vue3。

我正在运行 兼容性构建(Vue 3.1.0,使用 @vue/compat: 3.1.0)。

我有自定义按钮组件,如下:

<template>
  <button class="EditButton">
    <SvgIcon name="edit" />
    <span>Edit</span>
  </button>
</template>

<style scoped>
/* Some style */
</style>

我的应用当前调用这个按钮的方式是

<EditButton
  id="edit-button"
  :disabled="!aCondition"
  @click.native="editPost(post)"
/>

这样做可以正常工作。.native 事件修饰符在Vue 3中已被弃用,我已经移除了它(参见 vue3 v-on-native-modifier文档)。根据文档,v-on 监听器如果不是作为组件事件声明的一部分,应该作为DOM事件添加到第一个根元素上(在我的情况下是 button)。

然而这并不起作用。移除 .native 会在组件的 $attrs 中移除 onClick 监听器,以下是在开发者工具窗口中看到的结果: vuejs DevTools

下面是我的Vite配置,或许有用:

export default defineConfig({
  resolve: {
    alias: {
      "@": fileURLToPath(new URL("./src", import.meta.url)),
      vue: '@vue/compat'
    },
  },
  plugins: [
    vue({
      template: {
        compilerOptions: {
          compatConfig: {
            MODE: 2
          }
        }
      }
    }),
    createSvgIconsPlugin({
      iconDirs: [fileURLToPath(new URL("./src/assets/icons", import.meta.url))],
      symbolId: "icon-[name]",
    }),
  ]});

我的问题很简单,在Vue 3中如何处理点击事件,我到底哪里做错了?

解决方案

在Vue 3中,你应该移除 .native,但要确保组件在 emits 中没有声明 click

如果组件只有一个根元素,像你的 <button> 那样,这应该可行:

<EditButton
  id="edit-button"
  :disabled="!aCondition"
  @click="editPost(post)"
/>

并且组件可以保持为单根组件:

<template>
  <button class="EditButton">
    <SvgIcon name="edit" />
    <span>Edit</span>
  </button>
</template>

Vue 3会自动把未声明的属性和监听器透传到子组件的根元素。因此 iddisabled@click 应该透传到 button

通常导致这种透传失效的情况,是子组件将该事件声明为组件事件:

defineEmits(['click'])

或者,在Options API中:

emits: ['click']

一旦 click 被声明为组件事件,Vue就会把 @click 视为自定义组件事件,而不是原生DOM监听器,因此它不会自动附加到根元素 button

如果你确实希望 EditButton 显式暴露一个组件事件,请自行触发它:

<template>
  <button class="EditButton" @click="$emit('click', $event)">
    <SvgIcon name="edit" />
    <span>Edit</span>
  </button>
</template>

但如果你只需要原生按钮点击,不要在 emits 中声明 click;让Vue将监听器透传到根元素。

站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章