首页 / Vue3 入门教程 / 组件事件

Vue3 入门教程

组件事件

本教程共 40 篇 · 第 16 篇 · 更新于 2026-07-29 · 约 7 分钟阅读

VueVue3 入门教程组件事件$emitdefineEmits事件验证

16. 组件事件

本节目标:掌握组件的自定义事件系统,学会用 $emit 让子组件向父组件传递消息和数据。

为什么需要事件

Props 是父传子,那子传父靠什么?靠事件。

Vue 组件有一套自定义事件系统。子组件可以”发射”一个事件,父组件监听这个事件并做出响应。

触发事件

在模板里直接用 $emit

<!-- MyComponent.vue -->
<template>
  <button @click="$emit('someEvent')">Click Me</button>
</template>

<script setup> 里,$emit 不能直接用。你需要先定义,再用返回的函数触发:

<script setup>
const emit = defineEmits(['someEvent'])

function buttonClick() {
  emit('someEvent')
}
</script>
Tip

defineEmits() 返回一个函数,功能等价于 $emit。记住,它必须直接放在 <script setup> 的顶层,不能在函数内部调用。

监听事件

父组件用 v-on(缩写 @)来监听子组件发出的事件:

<MyComponent @some-event="callback" />

和 props 一样,事件名会自动转换大小写。子组件 emit 的是 camelCase someEvent,父组件监听用 kebab-case some-event

还支持 .once 修饰符——只触发一次:

<MyComponent @some-event.once="callback" />
Note

组件事件不会冒泡。你只能监听直接子组件的事件。兄弟组件或跨层级通信需要用事件总线或全局状态管理。

事件参数

$emit 第二个及以后的参数都会传给监听器:

<!-- 子组件 -->
<button @click="$emit('increaseBy', 1)">
  Increase by 1
</button>

父组件用箭头函数接收参数:

<MyButton @increase-by="(n) => count += n" />

或者直接用方法——值会作为第一个参数传入:

<MyButton @increase-by="increaseCount" />
// 父组件
function increaseCount(n) {
  count.value += n
}

也可以传多个参数:

<!-- $emit('submit', email, password, timestamp) -->
<MyForm @submit="handleSubmit" />
function handleSubmit(email, password, timestamp) {
  // 三个参数按顺序接收
}

声明要发出的事件

虽然不声明也能 emit,但建议总是声明。好处是:

  • 组件文档化——一眼看出它能发什么事件
  • Vue 可以把已知监听器从透传属性中排除
  • 支持事件验证
<script setup>
// 数组语法
defineEmits(['inFocus', 'submit'])
</script>

事件验证

和 prop 验证类似,用对象语法加验证函数:

<script setup>
const emit = defineEmits({
  // 不验证
  click: null,

  // 验证 submit 事件的载荷
  submit: ({ email, password }) => {
    if (email && password) {
      return true
    } else {
      console.warn('Invalid submit event payload!')
      return false
    }
  }
})

function submitForm(email, password) {
  emit('submit', { email, password })
}
</script>

验证函数接收 emit 的参数,返回 true 表示通过,false 表示不通过(会触发控制台警告)。

Note

如果 emits 选项里声明了原生事件名(比如 click),那监听器只会响应组件 emit 的 click,不再响应原生 click。

事件 vs Props:怎么选

场景方式
父传子Props
子传父事件
跨层级Provide / Inject

记住这个口诀:Props 往下传,事件往上传。这是 Vue 组件通信的基本模式。

一个完整的例子

子组件:

<!-- SubmitForm.vue -->
<script setup>
const emit = defineEmits({
  submit: ({ email, password }) => {
    return email && password
  }
})

function handleSubmit() {
  emit('submit', {
    email: 'test@example.com',
    password: '123456'
  })
}
</script>

<template>
  <button @click="handleSubmit">提交</button>
</template>

父组件:

<script setup>
import SubmitForm from './SubmitForm.vue'

function onSubmit({ email, password }) {
  console.log('收到提交:', email, password)
}
</script>

<template>
  <SubmitForm @submit="onSubmit" />
</template>

本节回顾

  • 子组件用 $emit('eventName', ...args) 触发事件
  • 父组件用 @event-name="handler" 监听
  • 事件名建议 camelCase 定义、kebab-case 监听
  • defineEmits() 声明事件,对象语法可加验证
  • 事件不会冒泡,只能监听直接子组件