Vue中的自定义事件

一、什么是自定义事件?

自定义事件 是 Vue 提供的一种机制,允许子组件通过 $emit 触发一个事件,父组件通过 v-on(即 @)监听该事件并执行回调。

它实现了 "子 → 父" 的数据传递,是 单向数据流 的重要补充。

核心 API

  • $emit(eventName, ...args):在子组件中触发事件
  • @event="handler":在父组件中监听事件

二、基本使用示例

1. 子组件触发事件

html 复制代码
<!-- ChildButton.vue -->
<template>
  <button @click="handleClick">
    点我触发事件
  </button>
</template>

<script setup>
// Vue 3: 使用 defineEmits 定义可触发的事件
const emit = defineEmits(['click', 'update'])

function handleClick() {
  // 触发 'click' 事件,可携带参数
  emit('click', { id: 1, name: '按钮1' })
  
  // 也可触发多个事件
  emit('update', Date.now())
}
</script>

defineEmits 是 Vue 3 <script setup> 中的宏,用于声明组件可触发的事件。


2. 父组件监听事件

html 复制代码
<!-- ParentComponent.vue -->
<script setup>
import ChildButton from './ChildButton.vue'

// 定义事件处理函数
function onChildClick(data) {
  console.log('子组件点击了:', data)
}

function onChildUpdate(timestamp) {
  console.log('子组件更新时间:', timestamp)
}
</script>

<template>
  <div>
    <h2>父组件</h2>
    <!-- 使用 v-on 监听子组件事件 -->
    <ChildButton 
      @click="onChildClick" 
      @update="onChildUpdate"
    />
  </div>
</template>

✅ 当子组件点击时,父组件的 onChildClick 函数会被调用。

三、Vue 2 vs Vue 3 写法对比

场景 Vue 2 写法 Vue 3 <script setup> 写法
触发事件 this.$emit('event', data) emit('event', data)
定义事件 (可选)emits: ['event'] const emit = defineEmits(['event'])
访问 emit this.$emit 通过 defineEmits 返回

Vue 2 示例

javascript 复制代码
// ChildComponent.vue
export default {
  methods: {
    handleClick() {
      this.$emit('click', 'Hello from child')
    }
  }
}
html 复制代码
<!-- Parent.vue -->
<ChildComponent @click="handleClick" />

✅ Vue 3 的写法更简洁、类型友好。

四、高级用法与最佳实践

1. 事件命名规范

  • 使用 kebab-case (短横线命名):@update-user@item-click
  • 避免使用原生 DOM 事件名(如 clickinput),除非你确实要覆盖

✅ 推荐:@update:modelValue@close-modal


2. v-model 的事件实现原理

v-model 本质上是 :modelValue + @update:modelValue 的语法糖。

子组件(可编辑输入框)
html 复制代码
<!-- CustomInput.vue -->
<script setup>
const emit = defineEmits(['update:modelValue'])
defineProps(['modelValue'])

function onInput(e) {
  emit('update:modelValue', e.target.value)
}
</script>

<template>
  <input 
    :value="modelValue" 
    @input="onInput" 
    type="text"
  />
</template>
父组件使用 v-model
html 复制代码
<script setup>
import CustomInput from './CustomInput.vue'
import { ref } from 'vue'

const inputValue = ref('')
</script>

<template>
  <!-- v-model 自动绑定 modelValue 和 update:modelValue -->
  <CustomInput v-model="inputValue" />
  <p>输入内容:{{ inputValue }}</p>
</template>

✅ 这就是 v-model 的工作原理!


3. 使用 defineEmits 进行类型校验(TypeScript)

html 复制代码
<script setup lang="ts">
// 定义事件类型
const emit = defineEmits<{
  (e: 'add', id: number): void
  (e: 'delete', id: number, reason: string): void
  (e: 'change', value: string): void
}>()

function handleAdd() {
  emit('add', 123) // 类型安全 ✅
}
</script>

✅ TypeScript 下,IDE 会自动提示事件名和参数类型。


4. 事件修饰符

Vue 支持事件修饰符,也可用于自定义事件:

html 复制代码
<!-- .once:只监听一次 -->
<ChildComponent @click.once="handleClick" />

<!-- .stop:阻止事件冒泡(较少用) -->
<ChildComponent @click.stop="handleClick" />

五、常见误区与解决方案

❌ 误区 1:在 <script setup> 中使用 this.$emit

javascript 复制代码
// ❌ 错误!<script setup> 中没有 this
this.$emit('click')

// ✅ 正确:使用 defineEmits
const emit = defineEmits(['click'])
emit('click')

❌ 误区 2:事件名使用 camelCase

html 复制代码
<!-- ❌ 不推荐 -->
<ChildComponent @itemClick="handle" />

<!-- ✅ 推荐 -->
<ChildComponent @item-click="handle" />

HTML 属性不区分大小写,建议统一使用 kebab-case。


❌ 误区 3:子组件直接修改 props

javascript 复制代码
// ❌ 错误!不要这样做
props.modelValue = 'new value'

// ✅ 正确:通过事件通知父组件
emit('update:modelValue', 'new value')

✅ 遵循单向数据流原则。

六、自定义事件 vs $attrs vs ref

通信方式 适用场景 方向 是否推荐
自定义事件 子 → 父 通知 子 → 父 推荐
$attrs 透传属性和事件 父 → 子 ✅ 适合高阶组件
ref 父 → 子 调用方法 父 → 子 ⚠️ 谨慎使用

📌 通信原则

  • 数据流:props 向下,events 向上
  • 避免过度使用 ref 调用子组件方法

七、总结

核心点 说明
作用 实现子组件向父组件通信
API $emit / defineEmits
语法 @event="handler"
Vue 3 defineEmits 更类型安全
v-model 基于 update:modelValue 事件
最佳实践 使用 kebab-case、避免修改 props

📌 一句话总结
自定义事件是 Vue 组件通信的"标准语言",掌握它,你才能构建可维护的组件体系

八、结语

感谢您的阅读!如果你有任何疑问或想要分享的经验,请在评论区留言交流!

相关推荐
yuanyxh3 小时前
Mac 软件推荐
前端·javascript·程序员
万少4 小时前
AtomCode开发微信小程序《谁去呀》 全流程
前端·javascript·后端
某人辛木4 小时前
Web自动化测试
前端·python·pycharm·pytest
Kagol4 小时前
Superpowers GSD gstack AgentSkills深度测评
前端·人工智能
excel5 小时前
JavaScript 字符串与模板字面量:从表象到本质理解
前端
京东云开发者6 小时前
当AI成为导演-如何用AI创作动漫短剧
前端
李白的天不白6 小时前
使用 SmartAdmin 进行前后端开发
java·前端
乘风gg6 小时前
🤡PUA AI Coding 工具 的 10 条终极语录
前端·ai编程·claude
学Linux的语莫6 小时前
Vue 3 入门教程
前端·javascript·vue.js
怕浪猫7 小时前
第一章、Chrome DevTools Protocol (CDP) 详解
前端·javascript·chrome