组件事件
本教程共 40 篇 · 第 16 篇 · 更新于 2026-07-29 · 约 7 分钟阅读
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()声明事件,对象语法可加验证 - 事件不会冒泡,只能监听直接子组件