VUE3入门很简单(5)---组件通信(自定义事件)

大家好!上回我们聊了 Props ------ 爸爸给娃塞零花钱的单向通道。但现实是:孩子也想说话啊!

比如:"爸,我零花钱花完了!"、"妈,帮我关下灯!"......在 Vue 世界里,这种"喊话"机制就是------自定义事件(Custom Events)

今天,我们就来揭开 defineEmits$emit 的神秘面纱,让你的子组件从此不再"哑巴"!


🗣️ 自定义事件是啥?一句话说清

自定义事件是子组件向父组件传递消息或数据的方式,通过触发事件"通知"父组件:"嘿,我这边有事!"

它和 Props 正好配对:

  • Props:父 → 子(传数据)
  • 自定义事件:子 → 父(传消息/请求)

合体后,就能实现双向通信,比如经典的"表单输入"、"弹窗关闭"、"列表项删除"等场景。


✨ 基本用法:三步教你"喊话"

第一步:子组件定义并触发事件

<script setup> 中,使用 defineEmits 声明你要发射哪些事件(可选但推荐),然后调用它来"喊话"。

html 复制代码
<!-- ChildButton.vue -->
<script setup>
// 声明这个组件会 emit 哪些事件(类型安全 + 文档化)
const emit = defineEmits(['father', 'mother'])

function callFather() {
  // 触发 click 事件,不带参数
  emit('father')
}

function callMother() {
  // 触发 confirm 事件,并传递数据
  emit('mother', { action: 'delete', id: 123 })
}
</script>

<template>
  <button @click="callFather">喊爸爸-给钱</button>
  <button @click="callMother">喊妈妈-关灯</button>
</template>

💡 defineEmits 是宏,无需 import!Vue 3 编译器自动处理。

第二步:父组件监听子组件的事件

在父组件中,像监听原生事件一样,用 @事件名 来接收:

html 复制代码
<!-- Parent.vue -->
<template>
  <ChildButton 
    @father="giveMoney" 
    @mother="turnOffLight" 
  />
</template>

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

function giveMoney() {
  console.log('给孩子零花钱')
}

function turnOffLight(payload) {
  console.log('收到孩子的请求:', payload) // { action: 'delete', id: 123 }
  // 这里可以调用 API 删除数据、更新状态等
}
</script>

第三步:跑起来!看控制台输出

点击喊爸爸-给钱喊妈妈-关灯按钮,你会看到:

完美!父子沟通无障碍!


⚠️ 注意事项:这些雷区别踩!

1. 不要直接修改父组件数据,而是"请求"

❌ 错误示范(试图绕过事件直接改):

js 复制代码
// 子组件里
props.user.name = '新名字' // 报错!Props 是只读的!

✅ 正确做法:

js 复制代码
// 子组件
emit('update:name', '新名字')

// 父组件
<UserCard @update:name="name = $event" />

2. 事件名用 kebab-case(短横线)更安全

虽然 Vue 支持驼峰(myEvent),但在模板中强烈建议用短横线命名,避免大小写问题:

vue 复制代码
<!-- 推荐 -->
<Child @close-dialog="handleClose" />

<!-- 不推荐(某些环境下可能失效) -->
<Child @closeDialog="handleClose" />

对应地,defineEmits 里写成字符串数组即可:

js 复制代码
defineEmits(['close-dialog'])

3. 事件不是万能的!别滥用

  • 如果多个组件都要响应同一个行为(比如全局通知),考虑用 Pinia
  • 如果只是父子之间简单交互,自定义事件是最轻量、最清晰的选择。

🎯 使用场景:什么时候该"喊话"?

场景 是否适合用自定义事件
子组件按钮点击,父组件执行逻辑 ✅ 经典用法
表单子组件提交数据 ✅ emit('submit', formData)
弹窗/抽屉关闭请求 ✅ emit('close')
列表项被删除 ✅ emit('delete', itemId)
全局状态变更(如用户登录) ❌ 用 Pinia 更合适
兄弟组件通信 ❌ 通过共同父组件中转,或用状态管理

🛠️ 实战示例:一个可关闭的通知卡片

html 复制代码
<!-- NotificationCard.vue -->
<script setup>
const emit = defineEmits(['close'])

function handleClose() {
  emit('close') // 告诉父组件:"我想消失!"
}
</script>

<template>
  <div class="notification">
    <span>🎉 恭喜你学会自定义事件了!</span>
    <button @click="handleClose">×</button>
  </div>
</template>

<style scoped>
.notification {
  display: flex;
  justify-content: space-between;
  padding: 12px;
  background: #e6f7ff;
  border: 1px solid #91d5ff;
}
</style>

父组件使用:

html 复制代码
<!-- App.vue -->
<template>
  <NotificationCard v-if="showNotification" @close="showNotification = false" />
</template>

<script setup>
import { ref } from 'vue'
import NotificationCard from './NotificationCard.vue'

const showNotification = ref(true)
</script>

点击 ×,通知消失!是不是超有成就感?


✅ 总结:自定义事件的黄金法则

  1. 子喊父听 :子组件用 emit 发送,父组件用 @ 监听。
  2. 命名规范 :事件名用短横线(close-dialog),别用驼峰。
  3. 传递数据:可以带任意参数(对象、字符串、数字等)。
  4. 配合 Props:实现完整的父子双向通信。

自定义事件,就像给孩子装了个对讲机------他不能直接改你钱包,但可以大声喊:"爸!再给我五块钱!"

而你,可以选择给,也可以选择拒绝(比如回一句:"自己赚去!" 😏)。

掌握它,你的 Vue 组件就能真正"活"起来!

相关推荐
张元清10 小时前
useEffect 之外:专门处理异步、深比较和 SSR 的 Effect Hook
前端·javascript·面试
XinZong11 小时前
OpenClaw 中最经典的 6 款skill,真正能进工作流的 skills
javascript·后端
XinZong11 小时前
2026 AI社交深度评测:InStreet 与 ClawReach 核心差异解析
javascript
Bug-制造者12 小时前
【Vue3 实战】全局错误处理体系搭建:实现业务与错误彻底解耦
前端·javascript·vue.js
竹林81812 小时前
从ethers.js迁移到Viem:我在DeFi Dashboard项目中踩过的坑与最终方案
javascript
zithern_juejin12 小时前
ES6——Promise
javascript
桜吹雪13 小时前
所有智能体架构(1):反思 (Reflection)
javascript·人工智能
前端那点事13 小时前
Vue3+TS 封装高复用 ECharts 通用组件,自适应+防抖+主题切换,开箱即用
前端·vue.js
前端那点事14 小时前
Vue3+TS动态路由终极方案|后端权限、刷新不丢、按钮权限、解决所有404BUG
前端·vue.js