事件处理 — CodeRoadMap事件处理
这一课你将能做到
- 用
v-on 绑定原生事件和自定义事件
- 使用事件修饰符控制事件行为
- 使用按键修饰符处理键盘事件
- 在父子组件间通过自定义事件通信
为什么要学
事件是用户与页面的「对话方式」。点击、输入、滚动、提交,全靠事件驱动。Vue 把原生 DOM 事件封装得更易用,还提供了修饰符这种「快捷方式」。
人话:事件就像「门铃」。用户按门铃(触发事件),你听到后去开门(执行函数)。修饰符就是门铃的「附加功能」:只响一次、只响特定铃声、阻止推销员等。
先建立图像
<script setup>
import { ref } from 'vue'
const count = ref(0)
function handleClick(e) {
console.log('事件对象', e)
count.value++
}
</script>
<template>
<button @click="handleClick">点击</button>
<button @click="count++">内联</button>
<button @click="(e) => console.log(e)">箭头函数</button>
</template>
跟着做
步骤 1:v-on 基础用法
<script setup>
import { ref } from 'vue'
const msg = ref('')
function greet(name) {
msg.value = `你好,${name}`
}
</script>
<template>
<!-- 方法名 -->
<button @click="greet('张三')">打招呼</button>
<!-- 内联语句 -->
<button @click="msg = '内联'">内联</button>
<!-- 访问原生事件对象 -->
<button @click="(e) => console.log(e.target)">事件对象</button>
</template>
步骤 2:事件修饰符
<template>
<!-- 阻止默认行为(如表单提交跳转) -->
<form @submit.prevent="onSubmit">...</form>
<!-- 阻止冒泡(不触发父元素事件) -->
<div @click="parent">
<button @click.stop="child">阻止冒泡</button>
</div>
<!-- 只触发一次 -->
<button @click.once="doOnce">只点一次</button>
<!-- 捕获模式(从外到内) -->
<div @click.capture="capture">捕获</div>
<!-- 只在事件目标是元素本身时触发 -->
<div @click.self="onlySelf">只有点自己才触发</div>
</template>
修饰符可以链式:@click.stop.prevent。
步骤 3:按键修饰符
<script setup>
function onEnter() { console.log('回车') }
function onEsc() { console.log('ESC') }
</script>
<template>
<!-- 特定按键 -->
<input @keyup.enter="onEnter" placeholder="回车触发">
<input @keyup.esc="onEsc" placeholder="ESC 触发">
<!-- 组合键 -->
<input @keyup.ctrl.enter="onEnter" placeholder="Ctrl+Enter">
<!-- 系统修饰键 -->
<button @click.ctrl="onCtrlClick">Ctrl+点击</button>
</template>
常用按键别名:.enter、.tab、.delete、.esc、.space、.up、.down、.left、.right。
步骤 4:自定义事件(子传父)
<!-- Child.vue -->
<script setup>
const emit = defineEmits(['update', 'delete'])
function save() {
emit('update', { id: 1, name: '新名称' })
}
</script>
<template>
<button @click="save">保存</button>
</template>
<!-- Parent.vue -->
<script setup>
function onUpdate(data) {
console.log('收到更新', data)
}
</script>
<template>
<Child @update="onUpdate" />
</template>
步骤 5:事件总线( mitt ,了解即可)
// bus.js
import mitt from 'mitt'
export const bus = mitt()
<!-- A 组件 -->
<script setup>
import { bus } from './bus'
bus.emit('refresh', { id: 1 })
</script>
<!-- B 组件 -->
<script setup>
import { bus } from './bus'
import { onMounted, onUnmounted } from 'vue'
function handler(data) { console.log(data) }
onMounted(() => bus.on('refresh', handler))
onUnmounted(() => bus.off('refresh', handler)) // 必须解绑
</script>
Vue 3 官方不再内置事件总线,推荐用 Pinia 或 provide/inject 替代。
讲清楚要点
v-on 简写 @:@click 是 v-on:click 的语法糖。
- 事件修饰符是语法糖:
.prevent 就是 e.preventDefault(),但写起来更简洁。
- 按键修饰符基于
event.key:.enter 判断 e.key === 'Enter'。
- 自定义事件是组件通信的核心:子组件通过
emit 发事件,父组件通过 @事件名 监听。
- 事件总线慎用:容易造成事件泛滥、难以追踪,优先考虑状态管理。
你可能会懵的地方
-
@click 和 @click.native 有什么区别?
Vue 3 中 @click 直接监听组件根元素的原生事件,不需要 .native 修饰符(Vue 2 需要)。
-
修饰符的执行顺序?
按书写顺序执行。@click.stop.prevent 先 stopPropagation,再 preventDefault。
-
怎么监听自定义组件的点击?
如果子组件根元素是原生元素,直接用 @click;如果子组件 emit 了自定义事件,用 @自定义事件名。
-
事件处理函数里怎么传参又拿事件对象?
@click="handler('参数', $event)",$event 是原生事件对象。
-
mitt 和 Vuex/Pinia 怎么选?
mitt 是轻量事件总线,适合跨组件临时通知;Pinia 是状态管理,适合共享数据。
错法对照
| 错法 | 问题 | 正解 |
|---|
@click="handler()" 加括号立即执行 | 页面加载就执行 | 写 @click="handler" 或 @click="handler()" 但明确意图 |
在模板里写 @click="handler(e)" 但没传 $event | e 是 undefined | @click="handler($event)" |
用 .stop 阻止了冒泡,父组件收不到事件 | 业务逻辑缺失 | 确认是否需要 .stop,或改用 .self |
组件上写 @click 但组件根元素是 Fragment | 事件不生效 | 确保组件有单一根元素,或子组件 emit |
| 滥用事件总线传递关键数据 | 数据流向不可追踪 | 用 Pinia 管理共享状态 |
卡点急救
- 事件不触发:检查方法名拼写,检查元素是否被其他元素遮挡(z-index、pointer-events)。
- 修饰符不生效:确认 Vue 版本,Vue 3 移除了
.native;确认修饰符顺序。
- 自定义事件收不到:检查
emit 的事件名和 @监听 的事件名是否完全一致(大小写敏感)。
面试常问
-
Vue 的事件修饰符有哪些?
.stop、.prevent、.capture、.self、.once、.passive、按键修饰符、系统修饰键。
-
怎么实现事件总线?
Vue 3 推荐用 mitt 库,或直接用 Pinia 的 action。核心原理是发布订阅模式。
-
v-model 和 @input 的关系?
v-model 是 :value="x" @input="x = $event.target.value" 的语法糖。
自测三问
@click.stop.prevent 中两个修饰符的执行顺序?
- 子组件向父组件传数据应该用什么方式?
- 什么场景下适合用事件总线而不是 Pinia?
小结一句话
事件是用户交互的入口,v-on 绑定事件,修饰符控制行为,自定义事件实现组件通信,掌握它们就能处理所有用户交互场景。
延伸