Vue3 事件:常用用法与开发高频坑点

前言:

Vue3 项目里,事件处理(@click、@change、@keyup 等)是最常见的内置事件。很多时候开发遇到的相关坑点问题不在"语法不会",而在传参、修饰符、target 区分、异步访问、类型推断 这些细节上。本文分享一下日常开发里真实会用到事件的写法,以及那些常见的报了诡异 bug、排查半天的坑。

一、事件的基础写法:

1. 最简单的用法

不传任何参数时,事件处理函数会自动收到原生事件对象作为第一个参数:

xml 复制代码
<template>
  <button @click="handleClick">点击</button>
</template>

<script setup>
  function handleClick(e) {
    console.log(e) // 原生 Event 对象
  }
</script>

2. 需要自定义参数 + 事件对象

只要传了自定义参数,就必须手动把 $event 一起传进去,否则事件对象会被覆盖掉:

xml 复制代码
<template>
  <button @click="handleClick('商品A', $event)">点击</button>
</template>

<script setup>
  function handleClick(name, e) {
    console.log(name) // '商品A'
    console.log(e.target) // 事件对象
  }
</script>

这是最经典的坑:带自定义参数却忘了 $event ,函数里拿到的 e 其实是自定义值,根本不是事件对象。

二、常用事件场景:

1. 获取当前点击的元素

xml 复制代码
<template>
  <div @click="handleClick">
    <span>内容</span>
  </div>
</div>

<script setup>
  function handleClick(e) {
    console.log(e.target) // 真正点击的元素(可能是 span)
    console.log(e.currentTarget) // 绑定事件的那个元素(div)
  }
</script>

target 和 currentTarget 是区分难度最高的两个概念,几乎每个项目都会有人踩坑:

  • e.target:用户实际点击的元素(子元素)
  • e.currentTarget:绑定事件监听的元素(父元素,始终不变)

嵌套结构里,点击 span 时:

  • target → span
  • currentTarget → div

业务上想拿"绑定事件的那个节点",务必用currentTarget。

2. 输入框的输入事件

xml 复制代码
<template>
  <input @input="handleInput" />
</template>

<script setup>
  function handleInput(e) {
    console.log(e.target.value) // 当前输入的值
  }
</script>

3. 键盘事件(回车、按键判断)

xml 复制代码
<template>
  <input @keyup="handleKey" />
</template>

<script setup>
  function handleKey(e) {
    if (e.key === 'Enter') {
      console.log('按下了回车')
    }
    if (e.key === 'Escape') {
      console.log('按下了 Esc')
    }
  }
</script>

4. 阻止冒泡和默认行为

优先用修饰符,简单清晰:

xml 复制代码
<!-- 阻止冒泡 -->
<div @click.stop="handleClick">...</div>

<!-- 阻止默认行为(如表单提交、a 标签跳转) -->
<form @submit.prevent="handleSubmit">...</form>

<!-- 组合 -->
<a href="..." @click.prevent.stop="handleClick">链接</a>

需要在函数里动态判断时,再用事件对象方法:

scss 复制代码
  function handleClick(e) {
    if (某种条件) {
      e.stopPropagation() // 阻止冒泡
    }
    
    if (另一种条件) {
      e.preventDefault() // 阻止默认行为
    }
  }

三、开发高频坑点汇总:

坑点 1:传了自定义参数却忘了 $event

xml 复制代码
<template>
  <!-- 错误 -->
  <button @click="handleClick(10)">点击</button>
</template>

<script setup>
  // 你以为 e 是事件对象,其实是 10
  function handleClick(e) {
    console.log(e.target)  // undefined,报错!
  }
</script>

解决:需要事件对象就手动传 $event 。

坑点 2:参数顺序写反

xml 复制代码
<template>
  <!-- 模板里顺序 -->
  <button @click="handle('A', $event)">点击</button>
</template>

<script setup>
  // 函数里顺序必须和模板完全一致
  function handle(name, e) {
    // 正确:name='A',e=事件对象
  }
</script>

顺序反了不会报错,但取值全乱,属于最隐蔽的坑。

坑点 3:target 和 currentTarget 混用

上面讲过,嵌套 DOM 里两者不同。用错了会导致:

  • 想拿父级数据,却取到子元素的属性(undefined)
  • 样式 / class 改到了错误节点

规则:凡是"绑定事件的那个元素",一律用 currentTarget 。

坑点 4:修饰符和函数内阻止逻辑重复

xml 复制代码
<template>
  <!-- 用了 .stop -->
  <div @click.stop="handleClick">...</div>
</template>

<script setup>
  function handleClick(e) {
    e.stopPropagation() // 又手动阻止一次
  }
</script>

重复阻止一般不会报错,但会让逻辑混乱、排查困难。建议统一规范:

  • 简单、固定的场景:用模板修饰符
  • 需要条件判断的动态场景:只用函数内方法,别混用

坑点 5:异步回调里直接使用事件对象

javascript 复制代码
  function handleClick(e) {
    setTimeout(() => {
      console.log(e.target)  // 可能拿到 null / undefined
    }, 500)
  }

原生事件对象在事件处理完后会被复用/回收,异步里再访问内容不稳定。

解决:先缓存需要的值。

javascript 复制代码
  function handleClick(e) {
    const el = e.target
    const val = e.target.value
    setTimeout(() => {
      console.log(el, val) // 用缓存值,安全
    }, 500)
  }

坑点 6:键盘事件用 keyCode 已废弃

ini 复制代码
// 老写法,已废弃
if (e.keyCode === 13) { ... }

用现代写法:

ini 复制代码
if (e.key === 'Enter') { ... }
if (e.code === 'Enter') { ... }
  • e.key:表示按键的字符 / 名称(如 'Enter'、'a'、'ArrowUp')
  • e.code:表示物理按键位置(如 'KeyA'、'ArrowUp')

区分中英文输入法布局时,用 e.code 更可靠。

坑点 7:事件绑定到组件标签上(组件事件)

给自定义组件用 @click 时,默认监听的是组件自定义事件,不是原生 DOM 事件:

xml 复制代码
<!-- 这样写,默认不会触发原生 click -->
<MyButton @click="handleClick" />

如果想让事件落到底层原生元素上,需要:

arduino 复制代码
<MyButton @click="handleClick" @click.capture="..." />

或者在组件内部显式 defineEmits:

scss 复制代码
// MyButton.vue
const emit = defineEmits(['click'])
function onClick(e) {
  emit('click', e)
}

简单记:原生 DOM 元素直接用 @click 没问题;自定义组件上想监听原生事件,要么组件内部 emit ,要么用 .capture 等原生修饰符。

四、事件常用修饰符一览:

常用按键快捷写法:

ini 复制代码
<input @keyup.enter="submit" />
<input @keyup.esc="cancel" />
<input @keyup.ctrl.enter="save" />

五、事件处理函数几种写法的取舍:

六、开发规范建议:

  1. 绑定事件元素的节点操作,一律用 currentTarget,避免嵌套踩坑。
  2. 需要事件对象 + 自定义参数时,别漏 $event ,别写反顺序。
  3. 固定阻止逻辑用修饰符,动态判断用函数内方法,别混用。
  4. 异步里先缓存事件对象的属性,别直接延迟访问。
  5. 键盘判断用 e.key / e.code ,别用废弃的 keyCode 。
  6. 自定义组件的事件按"组件事件"处理,要原生事件就内部 emit 或加原生修饰符。
相关推荐
weixin_446260851 小时前
ReAct‑Evolve:基于轨迹反馈的ReAct智能体自主提示演化系统
前端·react.js·前端框架
其实防守也摸鱼1 小时前
存储管理:页面置换算法学习任务(测验题要求给出置换页面顺序,缺页次数,缺页率)
android·前端·数据库·学习·oracle·自动化
怒放的生命19911 小时前
前端项目 Docker 打包实操指南
前端·docker·容器
tedcloud1231 小时前
Docker Compose 部署 Immich:手机照片自动备份与常见故障排查
运维·前端·docker·容器·智能手机·开源·流程图
时间的情敌1 小时前
《WebGIS 前端的成长路线,三维可视化工程师需要掌握什么》
前端·状态模式
漫步人生走在路上1 小时前
用小语种SEO突破外贸B2B天花板:法语、德语市场的实测数据
开发语言·前端·javascript·人工智能·搜索引擎·ecmascript
变与不变8061 小时前
jQuery 核心语法与 jQuery AJAX
前端·ajax·jquery
文章永久免费只为良心1 小时前
Ctfshow_Web应用安全与防护_第二章_无字母数字命令执行
前端·python·安全·网络安全·渗透测试·ctf·红队渗透测试