Vue3事件处理

Vue 3 事件处理完全指南

本文档从零开始讲解 Vue 3 的事件处理:如何绑定点击、输入、提交等事件,如何传参、如何拿到原生事件对象,以及事件修饰符按键修饰符系统修饰符 等,配有大量示例,适合新手系统学习。


一、什么是“事件处理”?

1.1 生活中的类比

页面上用户的操作(点击按钮、输入文字、按下回车、提交表单)都会触发浏览器的事件
我们要在代码里“监听”这些事件,并执行自己的逻辑(例如发请求、改数据、弹窗)。
在 Vue 里,用 v-on 或简写 @ 把“事件名”和“要执行的代码”绑在一起,这就是事件处理

1.2 基本写法

语法: v-on:事件名="处理方式"@事件名="处理方式"

  • 事件名:如 clickinputsubmitkeyup 等,和原生 DOM 事件名一致。
  • 处理方式:可以是一句内联表达式、一个方法名、或调用方法并传参
<template>
  <button @click="count++">点击了 {{ count }} 次</button>
</template>

<script setup>
import { ref } from 'vue'
const count = ref(0)
</script>
  • @click 表示“监听点击事件”。
  • “count++” 是内联表达式,点击一次就执行一次,count 加 1,页面自动更新。

下面从“内联 vs 方法”“传参”“事件对象”开始,再讲修饰符。


二、内联处理 vs 方法处理

2.1 内联处理器(直接写表达式)

在模板里直接写一句或几句表达式,用逗号或分号分隔(一般不写多句,可读性差):

<template>
  <div>
    <button @click="count++">+1</button>
    <button @click="count--">-1</button>
    <button @click="visible = !visible">切换显示</button>
  </div>
</template>

<script setup>
import { ref } from 'vue'
const count = ref(0)
const visible = ref(true)
</script>

适合非常简单的逻辑;逻辑一多就建议改成方法

2.2 方法处理器(写方法名)

把逻辑写在 script 里的函数中,模板里只写函数名(不写括号表示“引用这个函数”,点击时由 Vue 调用):

<template>
  <button @click="handleClick">点击</button>
</template>

<script setup>
function handleClick() {
  console.log('被点了')
  alert('你好')
}
</script>
  • @click=”handleClick”:点击时调用 handleClick(),不传参。
  • 若写成 @click=”handleClick()”,也会在点击时执行,但含义是“执行 handleClick 的返回值”;若返回值不是函数,一般不这样写。传参时必须写括号,见下一小节。

2.3 调用方法并传参

需要传参时,必须写括号,在括号里写参数;需要事件对象时,用 $event 传进去:

<template>
  <div>
    <button @click="say('你好')">打招呼</button>
    <button @click="add(1, 2)">1+2</button>
    <button @click="handleWithEvent($event)">需要事件对象</button>
  </div>
</template>

<script setup>
function say(msg) {
  alert(msg)
}
function add(a, b) {
  alert(a + b)
}
function handleWithEvent(e) {
  console.log(e.target)
}
</script>
  • $event 是 Vue 提供的占位变量,表示“当前这个 DOM 事件的 event 对象”,只在事件绑定里有效。
  • 若既要传参又要事件对象,可写:@click="handle(id, $event)",在方法里用第二个参数接收 e

三、事件对象($event)详解

3.1 什么时候需要事件对象?

例如:

  • 知道被点击的是哪个元素(e.target)
  • 阻止默认行为(如阻止表单提交、链接跳转)
  • 阻止冒泡(e.stopPropagation)
  • 拿到键盘事件的键码(e.key、e.keyCode)

@事件名 的表达式里,用 $event 代表“当前这次事件的 event 对象”,传给方法即可。

3.2 只传事件对象

<template>
  <button @click="onClick($event)">点击</button>
</template>

<script setup>
function onClick(e) {
  console.log(e.target)
  console.log(e.type)
}
</script>

3.3 既传业务参数又传事件对象

<template>
  <button @click="onDelete(item.id, $event)">删除</button>
</template>

<script setup>
function onDelete(id, e) {
  if (confirm('确定删除?')) {
    console.log('删除 id:', id)
  }
  e.preventDefault()
}
</script>

四、事件修饰符(常用)

在事件名后加 点 + 修饰符名,可以省去在方法里手写 e.preventDefault()e.stopPropagation() 等,让模板更简洁。

4.1 .prevent —— 阻止默认行为

等价于在方法里写 e.preventDefault()
常用在:表单提交(不刷新整页)、链接点击(不跳转)。

<template>
  <form @submit.prevent="onSubmit">
    <input type="text" />
    <button type="submit">提交</button>
  </form>
  <a href="https://vuejs.org" @click.prevent="go">去 Vue 官网</a>
</template>

<script setup>
function onSubmit() {
  console.log('表单提交逻辑,页面不会刷新')
}
function go() {
  console.log('不会跳转,可在这里做路由跳转等')
}
</script>

4.2 .stop —— 阻止冒泡

等价于 e.stopPropagation()
子元素点击时,事件不会继续“冒泡”到父元素,父元素上的点击监听不会触发。

<template>
  <div @click="onDivClick">
    <button @click.stop="onBtnClick">点我</button>
  </div>
</template>

<script setup>
function onDivClick() {
  console.log('div 被点了')
}
function onBtnClick() {
  console.log('只有按钮被点了,div 不会触发')
}
</script>

不加 .stop 时,点按钮会先打印“只有按钮…”,再打印“div 被点了”;加了 .stop 后只打印“只有按钮…”。

4.3 .once —— 最多触发一次

该事件只会触发一次,之后自动“解绑”,不再触发。

<template>
  <button @click.once="doOnce">只执行一次</button>
</template>

<script setup>
function doOnce() {
  console.log('只会出现一次')
}
</script>

4.4 .capture —— 使用捕获阶段

默认是冒泡阶段触发;加上 .capture 后,在捕获阶段就触发(从外到内)。
一般用得少,了解即可。

<div @click.capture="onCapture">...</div>

4.5 .self —— 仅当事件来自当前元素自身时才触发

只有 e.target 是当前元素自己时才执行处理函数;子元素触发的冒泡上来的事件不会触发当前元素。

<template>
  <div class="outer" @click.self="onSelf">
    <button>点我</button>
  </div>
</template>

<script setup>
function onSelf() {
  console.log('只有直接点击 div 区域(且不是按钮)才触发')
}
</script>

4.6 修饰符可以链式写

多个修饰符可以连写,顺序有时会影响结果(例如 .prevent 和 .stop 一般写在前):

<form @submit.prevent.stop="onSubmit">...</form>
<button @click.stop.prevent="onClick">...</button>

4.7 事件修饰符小结

修饰符 作用
.prevent 调用 e.preventDefault(),阻止默认行为
.stop 调用 e.stopPropagation(),阻止冒泡
.once 事件最多触发一次
.capture 使用捕获阶段
.self 仅当 e.target 是当前元素时触发
.passive 表示不调用 preventDefault,适合滚动性能优化(与 .prevent 不要同时用)

五、按键修饰符

监听键盘事件(如 keyup、keydown)时,经常只关心“按了哪个键”。
可以用 按键修饰符 限定:只有按下指定键时才触发。

5.1 常用按键别名

@keyup@keydown 后加 .键名

<template>
  <input
    type="text"
    @keyup.enter="onEnter"
    @keyup.esc="onEsc"
    @keyup.tab="onTab"
  />
</template>

<script setup>
function onEnter() {
  console.log('按了回车')
}
function onEsc() {
  console.log('按了 Esc')
}
function onTab() {
  console.log('按了 Tab')
}
</script>

常见别名:
enter、tab、delete(含 Backspace 和 Delete)、esc、space、up、down、left、right。

5.2 多个键组合(系统修饰符)

只有同时按下 修饰键(如 Ctrl、Alt、Shift、Meta)和普通键时才触发,可以用 .键名.修饰键

<template>
  <input @keyup.ctrl.enter="onCtrlEnter" />
</template>

<script setup>
function onCtrlEnter() {
  console.log('Ctrl + Enter')
}
</script>

系统修饰符: .ctrl、.alt、.shift、.meta(Windows 下 meta 多为 Win 键)。

5.3 .exact —— 精确匹配修饰键

.exact 表示“只有这些修饰键被按下,没有其它修饰键”时才触发:

<button @click.ctrl="onClick">Ctrl + 点击</button>
<button @click.ctrl.exact="onCtrlOnly">仅 Ctrl + 点击</button>
  • 第一个:Ctrl+点击 会触发;Ctrl+Shift+点击 也会触发。
  • 第二个:只有“仅 Ctrl + 点击”才触发,Ctrl+Shift+点击 不触发。

5.4 使用 key 字符串(自定义键)

若按键没有内置别名,可以用 key 的字符串(和 e.key 一致):

<input @keyup.page-down="onPageDown" />

六、鼠标按键修饰符

只在意“用鼠标哪个键点击”时,可加:
.left.right.middle(左键、右键、中键)。

<button @click.left="onLeft">左键</button>
<button @click.right="onRight">右键</button>

七、在组件上使用 v-on(自定义事件)

自定义组件上写的 @事件名,监听的是该组件 emit 出来的自定义事件,不是原生 DOM 事件。
若要监听组件根元素上的原生事件,需要子组件用 emits 暴露并再 emit,或在父组件用 .native(Vue 3 已移除 .native,改用 emits 或 $attrs)。

<template>
  <MyButton @click="onClick" />
</template>

这里的 click 若子组件通过 emit(‘click’) 触发,则 onClick 会执行;若子组件没有 emit(‘click’),则不会执行。
组件事件处理的详细用法见《Vue3组件.md》。


八、同时绑定多个处理函数

可以在表达式里写多个调用,用逗号分隔;或在一个方法里再调用其它方法:

<template>
  <button @click="fn1(); fn2()">多函数</button>
  <button @click="handleAll">一个方法里调多个</button>
</template>

<script setup>
function fn1() {
  console.log(1)
}
function fn2() {
  console.log(2)
}
function handleAll() {
  fn1()
  fn2()
}
</script>

实际开发中更推荐 一个方法 handleAll,逻辑更清晰。


九、常见场景示例

9.1 表单提交(阻止默认提交)

<template>
  <form @submit.prevent="onSubmit">
    <input v-model="name" placeholder="姓名" />
    <button type="submit">提交</button>
  </form>
</template>

<script setup>
import { ref } from 'vue'
const name = ref('')
function onSubmit() {
  console.log('提交:', name.value)
}
</script>

9.2 输入框回车搜索

<template>
  <input
    v-model="keyword"
    type="text"
    placeholder="输入后按回车搜索"
    @keyup.enter="search"
  />
</template>

<script setup>
import { ref } from 'vue'
const keyword = ref('')
function search() {
  console.log('搜索:', keyword.value)
}
</script>

9.3 列表项点击并拿到数据

<template>
  <ul>
    <li
      v-for="item in list"
      :key="item.id"
      @click="selectItem(item)"
    >
      {{ item.name }}
    </li>
  </ul>
</template>

<script setup>
import { ref } from 'vue'
const list = ref([
  { id: 1, name: '选项一' },
  { id: 2, name: '选项二' }
])
function selectItem(item) {
  console.log('选中', item)
}
</script>

9.4 阻止链接跳转并做路由

<template>
  <a href="/detail" @click.prevent="goDetail">查看详情</a>
</template>

<script setup>
import { useRouter } from 'vue-router'
const router = useRouter()
function goDetail() {
  router.push('/detail')
}
</script>

9.5 点击遮罩关闭弹窗(.self 示例)

<template>
  <div class="mask" @click.self="close">
    <div class="modal" @click.stop>
      弹窗内容
      <button @click="close">关闭</button>
    </div>
  </div>
</template>

<script setup>
const emit = defineEmits(['close'])
function close() {
  emit('close')
}
</script>
  • 遮罩(mask)会触发 close(.self 保证只有直接点 mask 才触发)。
  • 弹窗内部有 @click.stop,不会冒泡到 mask,不会误关。
  • 点“关闭”按钮也会执行 close。

十、易错点与注意点

10.1 方法名要不要括号

  • @click=”fn”:传的是函数引用,点击时由 Vue 调用,无参时常用。
  • @click=”fn()”:每次求值都会执行 fn();要传参传 $event 时必须写括号,如 @click=”fn(1, $event)”

10.2 $event 只在事件绑定里有效

$event 是模板里事件绑定表达式的“占位变量”,只在 v-on 的赋值里有效,在 {{ }} 或其它地方不能直接用。

10.3 修饰符顺序

多个修饰符连写时,顺序可能影响行为。常见写法:.stop.prevent 放前面,.once.self 等放后面;.capture.passive 按需求写。
.prevent.passive 不要同时用(语义冲突)。

10.4 逻辑不要堆在模板里

内联表达式适合 1~2 句简单逻辑;稍复杂就抽成方法,模板里只写 @事件=”方法名”@事件=”方法(参数)”,便于维护和测试。


十一、事件处理速查表

需求 写法
点击 @click=”fn” 或 @click=”fn()”
传参 @click=”fn(1, 2)”
需要事件对象 @click=”fn($event)” 或 @click=”fn(id, $event)”
阻止默认行为 @submit.prevent 或 @click.prevent
阻止冒泡 @click.stop
只触发一次 @click.once
回车键 @keyup.enter
Esc @keyup.esc
Ctrl+点击 @click.ctrl
精确修饰键 @click.ctrl.exact

十二、学习建议

  1. 先练熟 @click 的两种写法:无参用方法名,有参用 fn(参数),需要事件对象用 $event
  2. 记住常用事件修饰符.prevent(表单/链接)、.stop(冒泡)、.once(只触发一次)。
  3. 表单和输入框常用 @submit.prevent@keyup.enter
  4. 逻辑稍多就抽成方法,保持模板简洁。

把本文档里的示例在项目里敲一遍、改一改,会掌握得更牢。组件自定义事件见《Vue3组件.md》。祝你学习顺利。

发表评论