news 2026/5/2 11:31:45

Vue.js 组件 - 自定义事件

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Vue.js 组件 - 自定义事件

Vue.js 组件 - 自定义事件

自定义事件是 Vue 子组件向父组件通信的核心机制,子组件通过emit触发事件,父组件通过@事件名监听并响应。


一、基本用法

1. 定义与触发

<!-- 子组件 ChildButton.vue --> <script setup> // 声明可触发的事件 const emit = defineEmits(['click', 'submit']) const handleClick = () => { emit('click') // 无参数 } const handleSubmit = () => { emit('submit', { // 携带参数 id: 1, name: 'Vue' }) } </script> <template> <button @click="handleClick">点击</button> <button @click="handleSubmit">提交</button> </template>

2. 监听事件

<!-- 父组件 --> <template> <ChildButton @click="onClick" @submit="onSubmit" /> </template> <script setup> const onClick = () => { console.log('子组件被点击了') } const onSubmit = (data) => { console.log('提交数据:', data) // { id: 1, name: 'Vue' } } </script>

二、defineEmits 详解

1. 数组语法(最常用)

constemit=defineEmits(['update','delete','change'])

2. 对象语法(带验证)

constemit=defineEmits({// 无验证click:null,// 带验证 — 返回 false 时控制台输出警告(事件仍会触发)submit:(payload)=>{if(!payload.id){console.warn('submit 事件需要 id 字段')returnfalse}returntrue},// 多参数验证update:(id,newValue)=>{returntypeofid==='number'&&newValue!==undefined}})

3. TypeScript 类型声明

<script setup lang="ts"> const emit = defineEmits<{ (e: 'change', id: number): void (e: 'update', value: string): void (e: 'delete', id: number): void }>() // Vue 3.3+ 更简洁的写法 const emit = defineEmits<{ change: [id: number] update: [value: string] delete: [id: number] }>() </script>

三、事件参数传递

1. 单个参数

<!-- 子组件 --> <script setup> const emit = defineEmits(['select']) const onSelect = (item) => { emit('select', item) } </script> <!-- 父组件 --> <Child @select="onSelect" /> <script setup> const onSelect = (item) => { console.log(item) // { id: 1, name: 'Vue' } } </script>

2. 多个参数

<!-- 子组件 --> <script setup> const emit = defineEmits(['move']) const onMove = () => { emit('move', 100, 200) // 传递多个参数 } </script> <!-- 父组件 --> <Child @move="onMove" /> <script setup> const onMove = (x, y) => { console.log(x, y) // 100 200 } </script>

3. 内联箭头函数提取参数

<!-- 只需要第二个参数时 --> <Child @move="(x, y) => currentY = y" />

四、v-model 的自定义事件本质

1. 单个 v-model

<!-- 父组件 --> <CustomInput v-model="value" /> <!-- 等价于 --> <CustomInput :modelValue="value" @update:modelValue="value = $event" />
<!-- 子组件 CustomInput.vue --> <script setup> defineProps(['modelValue']) const emit = defineEmits(['update:modelValue']) </script> <template> <input :value="modelValue" @input="emit('update:modelValue', $event.target.value)" /> </template>

2. 具名 v-model(多个双向绑定)

<!-- 父组件 --> <UserForm v-model:name="userName" v-model:age="userAge" /> <!-- 等价于 --> <UserForm :name="userName" @update:name="userName = $event" :age="userAge" @update:age="userAge = $event" />
<!-- 子组件 UserForm.vue --> <script setup> defineProps(['name', 'age']) const emit = defineEmits(['update:name', 'update:age']) </script> <template> <input :value="name" @input="emit('update:name', $event.target.value)" /> <input :value="age" @input="emit('update:age', Number($event.target.value))" /> </template>

3. 自定义 v-model 修饰符

<!-- 父组件 --> <CustomInput v-model.capitalize="title" />
<!-- 子组件 CustomInput.vue --> <script setup> const props = defineProps({ modelValue: String, modelModifiers: { default: () => ({}) } }) const emit = defineEmits(['update:modelValue']) const emitValue = (e) => { let value = e.target.value // 检查修饰符 if (props.modelModifiers.capitalize) { value = value.charAt(0).toUpperCase() + value.slice(1) } emit('update:modelValue', value) } </script> <template> <input :value="modelValue" @input="emitValue" /> </template>

4. 具名 v-model 的修饰符

<!-- 父组件 --> <UserForm v-model:name.trim="userName" />
<!-- 子组件 --> <script setup> const props = defineProps({ name: String, nameModifiers: { default: () => ({}) } // 命名规则:prop名 + Modifiers }) const emit = defineEmits(['update:name']) const handleInput = (e) => { let value = e.target.value if (props.nameModifiers.trim) { value = value.trim() } emit('update:name', value) } </script>

五、事件命名规范

推荐使用 kebab-case

// ✅ 推荐constemit=defineEmits(['update-item','delete-user'])// ❌ 不推荐(HTML 不区分大小写,camelCase 会被转为小写导致监听失败)constemit=defineEmits(['updateItem','deleteUser'])
<!-- 父组件监听 --> <Child @update-item="handler" /> <!-- ✅ --> <Child @updateItem="handler" /> <!-- ❌ 可能监听不到 -->

常见事件命名约定

场景事件名说明
值更新update:propNamev-model 约定
删除delete/remove删除操作
变更change值变化后触发
输入input输入过程中触发
确认confirm确认操作
取消cancel取消操作
提交submit表单提交

六、实战场景

1. 表单组件双向绑定

<!-- SearchBar.vue --> <script setup> const props = defineProps({ modelValue: String, placeholder: { type: String, default: '搜索...' } }) const emit = defineEmits(['update:modelValue', 'search']) const onInput = (e) => { emit('update:modelValue', e.target.value) } const onSearch = () => { emit('search', props.modelValue) } </script> <template> <div class="search-bar"> <input :value="modelValue" :placeholder="placeholder" @input="onInput" @keyup.enter="onSearch" /> <button @click="onSearch">搜索</button> </div> </template>
<!-- 父组件 --> <template> <SearchBar v-model="keyword" @search="doSearch" /> </template> <script setup> import { ref } from 'vue' const keyword = ref('') const doSearch = (val) => { console.log('搜索:', val) } </script>

2. 列表项操作

<!-- TodoItem.vue --> <script setup> defineProps({ todo: Object }) const emit = defineEmits(['toggle', 'delete', 'edit']) const onToggle = () => emit('toggle', todo.id) const onDelete = () => emit('delete', todo.id) const onEdit = (e) => emit('edit', todo.id, e.target.value) </script> <template> <li :class="{ done: todo.done }"> <input type="checkbox" :checked="todo.done" @change="onToggle" /> <span>{{ todo.text }}</span> <button @click="onDelete">删除</button> </li> </template>
<!-- 父组件 --> <template> <TodoItem v-for="todo in todos" :key="todo.id" :todo="todo" @toggle="toggleTodo" @delete="deleteTodo" @edit="editTodo" /> </template> <script setup> const toggleTodo = (id) => { /* ... */ } const deleteTodo = (id) => { /* ... */ } const editTodo = (id, newText) => { /* ... */ } </script>

3. 对话框组件

<!-- Dialog.vue --> <script setup> defineProps({ modelValue: Boolean, // 控制显示/隐藏 title: String }) const emit = defineEmits(['update:modelValue', 'confirm', 'cancel']) const close = () => emit('update:modelValue', false) const onConfirm = () => { emit('confirm') close() } const onCancel = () => { emit('cancel') close() } </script> <template> <div v-if="modelValue" class="dialog-overlay" @click.self="close"> <div class="dialog"> <h3>{{ title }}</h3> <slot></slot> <div class="dialog-footer"> <button @click="onCancel">取消</button> <button @click="onConfirm">确定</button> </div> </div> </div> </template>
<!-- 父组件 --> <template> <Dialog v-model="visible" title="确认删除" @confirm="onConfirm" @cancel="onCancel" > <p>确定要删除该项吗?</p> </Dialog> </template>

七、注意事项

要点说明
命名用 kebab-caseemit('update-item')而非emit('updateItem'),避免大小写问题
事件是单向的emit只能向上传递,父组件监听是被动响应
验证不阻止触发defineEmits验证返回false仅输出警告,事件仍会触发
避免修改 props需要修改数据时用emit通知父组件,而非直接改 props
v-model 事件名必须是update:propName格式,这是 Vue 的约定
修饰符 propmodelModifiers/nameModifiers是 Vue 保留的 prop 名
$event 位置内联@event="handler($event)"$event是事件回调的第一个参数
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/2 11:28:51

技能化框架设计:从插件化架构到自动化任务编排

1. 项目概述与核心价值最近在开源社区里&#xff0c;我注意到一个名为polaroteam/moltdj-skill的项目。这个标题乍一看&#xff0c;像是一个特定团队&#xff08;polaroteam&#xff09;开发的、与“DJ”技能相关的工具或框架。对于从事自动化、机器人流程自动化&#xff08;RP…

作者头像 李华
网站建设 2026/5/2 11:27:24

如何用QueryExcel解决Excel文件批量查询难题

如何用QueryExcel解决Excel文件批量查询难题 【免费下载链接】QueryExcel 多Excel文件内容查询工具。 项目地址: https://gitcode.com/gh_mirrors/qu/QueryExcel 还在为在几十个Excel文件中查找某个客户信息而头疼吗&#xff1f;还在为重复的CtrlF操作而疲惫不堪吗&…

作者头像 李华
网站建设 2026/5/2 11:24:34

LinkSwift:浏览器脚本实现多平台网盘直链下载的完整指南

LinkSwift&#xff1a;浏览器脚本实现多平台网盘直链下载的完整指南 【免费下载链接】Online-disk-direct-link-download-assistant 一个基于 JavaScript 的网盘文件下载地址获取工具。基于【网盘直链下载助手】修改 &#xff0c;支持 百度网盘 / 阿里云盘 / 中国移动云盘 / 天…

作者头像 李华