×

Vue3 组件事件

独孤求败 独孤求败 发表于2026-07-07 14:12:49 浏览35 评论0

抢沙发发表评论

在 Vue3 中,组件化开发已经成为项目开发的基础能力。

当一个组件被拆分出来以后,组件之间就需要进行数据与行为的传递:

  • 父组件 → 子组件:使用 Props
  • 子组件 → 父组件:使用 Events(组件事件)
  • 双向绑定:使用 v-model

很多初学者学习 Vue3 时,往往只关注 Props,却忽略了组件事件的重要性。

实际上,在企业级项目开发中:

  • 表单组件提交
  • 弹窗确认
  • 表格操作
  • 搜索组件
  • 自定义输入框
  • 上传组件

几乎都离不开组件事件。

Vue3 对组件事件进行了进一步规范化,引入了 emits 配置,使组件通信更加清晰、安全、可维护。

本文将系统讲解 Vue3 组件事件的所有核心知识点,包括:

  • 组件事件基础
  • emit 触发事件
  • 父组件监听事件
  • 事件传参
  • defineEmits
  • emits 配置
  • 事件校验
  • v-model 底层原理
  • update:modelValue
  • 多个 v-model
  • 自定义 v-model
  • 企业级开发规范

一、什么是组件事件

组件事件本质上是一种:

子组件通知父组件的通信机制

例如:

父组件:

<UserForm />

用户点击提交按钮:

<button>提交</button>

此时子组件需要告诉父组件:

用户点击了提交按钮

这时候就需要:

emit()

触发一个组件事件。


二、触发组件事件

Vue 提供:

$emit()

用于触发自定义事件。

例如:

<template>
  <button @click="$emit('save')">
    保存
  </button>
</template>

当按钮点击时:

$emit('save')

会向父组件发送一个事件。


三、父组件监听事件

子组件:

<button @click="$emit('save')">
  保存
</button>

父组件:

<UserForm @save="handleSave" />
const handleSave = () => {
  console.log("执行保存")
}

执行流程:

点击按钮
    ↓
emit('save')
    ↓
父组件监听到 save
    ↓
执行 handleSave

四、事件名称规范

推荐使用:

kebab-case

例如:

emit("user-login")

父组件:

<UserForm @user-login="login" />

虽然 Vue 支持自动转换:

emit("userLogin")

监听:

<UserForm @user-login="login" />

依然可以正常工作。

但是企业开发统一推荐:

user-login
form-submit
page-change
data-refresh

不要写:

userLogin
formSubmit

这样更符合 HTML 属性命名规范。


五、事件传递参数

实际开发中,事件通常需要携带数据。

例如:

<button @click="$emit('delete', 1001)">
  删除
</button>

父组件:

<UserTable @delete="handleDelete" />
const handleDelete = (id) => {
  console.log(id)
}

输出:

1001

六、传递多个参数

子组件:

emit(
  "submit",
  username,
  password
)

父组件:

const submit = (
  username,
  password
) => {

}

接收:

zhangsan
123456

七、传递对象参数

企业项目中最常见。

子组件:

emit("submit", {
  username"admin",
  password"123456"
})

父组件:

const submit = (form) => {
  console.log(form)
}

输出:

{
  username:"admin",
  password:"123456"
}

这种方式扩展性最好。


八、defineEmits

在 Composition API 中:

<script setup>
</script>

无法直接使用:

this.$emit

需要:

defineEmits()

例如:

<script setup>

const emit = defineEmits([
  "save"
])

const submit = () => {
  emit("save")
}

</script>

这也是 Vue3 官方推荐写法。


九、defineEmits 数组写法

最简单方式:

const emit = defineEmits([
  "save",
  "delete",
  "update"
])

表示组件允许触发:

save
delete
update

十、对象写法

Vue3 推荐大型项目使用对象写法。

const emit = defineEmits({
  save:null,
  delete:null
})

这样便于扩展事件校验。


十一、事件校验

类似 Props 校验。

例如:

const emit = defineEmits({

  submit:(data)=>{

    return !!(
      data.username &&
      data.password
    )

  }

})

触发:

emit("submit", {
  username:"admin",
  password:"123456"
})

校验通过。

如果:

emit("submit", {})

控制台警告。


十二、Options API 中的 emits

除了 Composition API。

Options API 也支持:

export default {

  emits:[
    "save"
  ]

}

触发:

this.$emit("save")

Vue3 新增了 emits 选项,用于明确声明组件可以发出的事件。


十三、为什么要声明 emits

很多人疑惑:

直接 emit 不行吗?

可以。

但声明后有很多优势:

IDE 自动提示

emit("save")

拥有代码提示。


防止拼写错误

错误:

emit("svae")

声明:

emits:["save"]

Vue 可以发现问题。


组件接口更清晰

类似:

props:[]

定义输入。

emits:[]

定义输出。

形成完整组件契约


十四、事件只会向父组件传递

很多新手认为:

emit()

类似 DOM 冒泡。

实际上:

不会自动冒泡

组件事件只会:

子组件
   ↓
父组件

不会:

子组件
 ↓
父组件
 ↓
爷组件

如果需要继续传递:

再次 emit

即可。社区讨论中也经常提到这一点,自定义组件事件不会像原生 DOM 事件那样自动向上冒泡。


十五、监听一次事件

Vue 支持:

<UserForm
  @save.once="save"
/>

只执行一次。

适用于:

首次加载
首次授权
首次确认

场景。


十六、v-model 本质就是事件

很多人认为:

<UserInput
  v-model="name"
/>

是特殊语法。

实际上它最终会转换成:

<UserInput
  :modelValue="name"
  @update:modelValue="
     name = $event
  "
/>

也就是说:

v-model
=
Prop
+
事件

这是 Vue3 组件双向绑定的核心机制。


十七、自定义 v-model

子组件:

<script setup>

const props =
  defineProps([
    "modelValue"
  ])

const emit =
  defineEmits([
    "update:modelValue"
  ])

</script>
<input
 :value="modelValue"
 @input="
 emit(
   'update:modelValue',
   $event.target.value
 )
"
/>

父组件:

<MyInput
  v-model="username"
/>

即可实现双向绑定。


十八、Vue3.4 的 defineModel

Vue3.4 开始新增:

defineModel()

例如:

const model =
  defineModel()

直接获得:

modelValue
+
update:modelValue

能力。

代码大幅简化。


十九、多个 v-model

Vue3 支持:

<UserName
  v-model:first-name="first"
  v-model:last-name="last"
/>

对应:

defineModel("firstName")

defineModel("lastName")

一个组件拥有多个双向绑定能力。


二十、企业级项目常见事件设计

表格组件

emit("page-change")
emit("sort-change")
emit("selection-change")

搜索组件

emit("search")
emit("reset")

弹窗组件

emit("confirm")
emit("cancel")

上传组件

emit("success")
emit("error")

表单组件

emit("submit")
emit("validate")

二十一、企业级开发规范

必须声明 emits

推荐:

defineEmits([
  "save",
  "delete"
])

不要直接:

emit("save")

而不声明。


事件名统一使用 kebab-case

推荐:

page-change
row-click
form-submit

不要:

pageChange
rowClick

参数统一对象化

推荐:

emit("save", {
  id:1,
  name:"Tom"
})

不要:

emit(
  "save",
  1,
  "Tom",
  true
)

后期维护困难。


不要滥用事件链

错误:

A

B

C

D

层层 emit。

复杂项目建议:

Pinia
Provide/Inject
状态管理

统一处理共享状态。


总结

Vue3 的组件事件是组件通信体系中的核心组成部分。

其核心思想非常简单:

Props 负责输入
Events 负责输出

开发流程通常为:

父组件传递 Props
        ↓
子组件接收数据
        ↓
用户触发操作
        ↓
emit 发送事件
        ↓
父组件监听事件
        ↓
更新状态

而 Vue3 又进一步通过:

defineEmits
emits
事件校验
v-model
defineModel

构建出完整且规范的组件通信机制。

掌握组件事件之后,再结合:

Props
Slots
Provide/Inject
Pinia


群贤毕至

访客