
一、事件在 Vue 3 中处理事件非常简单核心就是 v-on 指令我们通常用它的简写 符号。基础用法绑定一个点击事件这是最常见的场景比如点击按钮弹出一个提示框。实际场景用户点击“提交”按钮触发一个提交表单的函数。template!-- 使用 click 绑定点击事件 --buttonclickhandleSubmit提交表单/button/templatescriptsetup// Vue 3 推荐使用 setup 语法糖consthandleSubmit(){console.log(表单已提交);}/script二、事件对象场景一只获取事件对象不传其他参数实际开发场景你想做一个“点击按钮显示鼠标点击在屏幕哪个位置”的功能。当你不传任何参数时Vue 会自动把原生的事件对象Event作为第一个参数传给你的函数。你可以直接用一个变量比如 event 或 e来接收它。template!-- 直接写函数名不要加括号 --buttonclickhandleClick点我看鼠标位置/button/templatescriptsetupconsthandleClick(event){// event 就是原生事件对象里面包含了所有点击信息console.log(点击的X坐标:,event.clientX);console.log(点击的Y坐标:,event.clientY);}/script场景二只传递自定义参数不需要事件对象实际开发场景你有一个商品列表点击“加入购物车”按钮时你需要把商品的 id 传给后台。template!-- 调用函数时直接把参数写在括号里 --buttonclickaddToCart(1001)加入购物车/button/templatescriptsetupconstaddToCart(productId){console.log(把商品ID:,productId,加入购物车);}/script场景三既要传自定义参数又要获取事件对象核心重点 ⭐实际开发场景你有一个商品列表点击“加入购物车”时既需要知道商品ID又需要阻止浏览器默认的跳转行为比如按钮被包裹在 a 标签里。这时候如果你直接写clickaddToCart(1001, event)Vue 会找不到event变量。你必须使用 Vue 提供的特殊变量$event来代表原生事件对象template!-- 重点看这里自定义参数写在前面$event 写在后面 --buttonclickaddToCart(1001, $event)加入购物车/button/templatescriptsetup// 接收参数时顺序要和模板里保持一致constaddToCart(productId,event){console.log(商品ID:,productId);// 使用事件对象的方法比如阻止默认行为event.preventDefault();console.log(阻止了默认跳转行为);}/script$event是什么 它是 Vue 提供的一个“占位符”专门用来在模板里表示“原生事件对象”。什么时候用$event 只有当你既想传自己的参数又想拿事件对象时才需要手动写$event。参数顺序别搞反模板里写clickhandle(参数1, $event)函数里就要写handle(参数1, event)一一对应。三、事件修饰符1、什么是事件修饰符在原生 JavaScript 中我们处理事件时经常需要写event.preventDefault()阻止默认行为比如阻止表单提交刷新页面或者event.stopPropagation()阻止事件冒泡。这会让你的业务逻辑代码里掺杂很多 DOM 操作的细节。Vue 为了解决这个问题为 v-on即 提供了一系列以点.开头的后缀这就是事件修饰符。它让你可以在模板层面直接处理这些 DOM 细节让你的 JS 方法保持纯粹。核心事件修饰符.stop阻止事件冒泡作用点击内部元素时不会触发外部元素的点击事件。实际场景在一个大卡片里有个“关闭”小按钮。你点击关闭按钮时只希望关闭卡片而不希望触发整个卡片的点击跳转。!-- 点击这个 li 时只会触发 handleLi不会触发 handleUl --ulclickhandleUlliclick.stophandleLi点击我不会冒泡给 ul/li/ul.prevent阻止默认行为作用阻止浏览器默认的事件行为。实际场景点击链接不想跳转或者提交表单时不想刷新页面。!-- 点击后不会跳转到百度只会执行 handleClick --aclick.preventhandleClickhrefhttps://www.baidu.com点我不会跳转/a!-- 提交表单时不再重载页面 --formsubmit.preventonSubmitbuttontypesubmit提交/button/form.once事件只触发一次作用绑定的事件回调函数只会被执行一次之后自动解绑。实际场景防止用户连续点击“提交订单”按钮导致重复下单。buttonclick.oncesubmitOrder确认支付只能点一次/button.self只当事件从元素本身发出时才触发作用如果点击的是该元素的子元素不会触发该事件。实际场景点击弹窗的“灰色遮罩层”关闭弹窗但点击弹窗“内部的内容”时不关闭。divclassmaskclick.selfcloseModaldivclassmodal-content点我这里不会关闭弹窗/div/div.capture使用事件捕获模式作用改变事件流的默认顺序默认是冒泡从内向外变成从外向内触发。实际场景需要在内部元素处理事件之前先在外层拦截或做一些全局处理。!-- 内部元素触发的事件会先在此处处理然后才交由内部元素处理 --divclick.capturehandleCapturebuttonclickhandleBtn按钮/button/div.passive提升滚动性能作用告诉浏览器“我保证不会调用 preventDefault()”让浏览器立即触发默认的滚动行为不用等待 JS 执行完毕。实际场景移动端长列表滚动优化。divscroll.passiveonScroll!-- 大量列表内容 --/div3、进阶用法修饰符可以“串联”使用事件修饰符是可以链式调用的但顺序很重要Vue 会按照你写的顺序依次执行。click.prevent.self会阻止所有的点击包括子元素的点击的默认行为但只有点击自身时才触发回调。click.self.prevent只有点击元素自身时才会阻止默认行为并触发回调。四、按键修饰符在原生 JS 中判断用户按下了哪个键需要写一堆if (event.keyCode 13)这样的逻辑而在 Vue 中你只需要加个小后缀就能搞定。在 Vue 3 中按键修饰符主要分为常用按键别名、自定义按键和系统按键组合三种。1、常用按键别名最常用Vue 为最常用的按键提供了非常直观的英文别名比如.enter、.esc、.tab、.space等。常用的别名还有.tab、.delete、.esc、.space、.up、.down、.left、.right。实际开发场景用户在搜索框输入关键词后按下回车键直接触发搜索。template!-- 只有按下 Enter 键时才会触发 handleSearch --inputv-modelkeywordkeyup.enterhandleSearchplaceholder输入关键词按回车搜索//templatescriptsetupimport{ref}fromvue;constkeywordref();consthandleSearch(){console.log(开始搜索:,keyword.value);}/script2、自定义按键直接使用按键名如果 Vue 没有提供某个按键的别名在 Vue 3 中你可以直接将KeyboardEvent.key暴露的按键名称转换为 kebab-case短横线命名作为修饰符。实际开发场景做一个文档阅读器用户按下 PageDown 键时自动向下翻页。注在 Vue 2 中可能需要用 keyCode但在 Vue 3 中推荐直接用按键名转短横线形式更语义化template!-- 监听 PageDown 键 --divkeyup.page-downnextPage文档内容.../div/template3、系统按键修饰符组合键你可以配合.ctrl、.alt、.shift、.meta来监听组合键。实际开发场景按住 Ctrl 键点击某个按钮触发“另存为”而不是普通的“保存”。在富文本编辑器中按下 Alt Enter 换行。.meta键在 Mac 键盘上是 Command 键 (⌘)在 Windows 键盘上是 Windows 键 (⊞)template!-- Ctrl 点击 --buttonclick.ctrlsaveAs另存为/button!-- Alt Enter --textareakeyup.alt.enterhandleNewLine/textarea/template4、.exact 修饰符.exact修饰符是 Vue 事件处理中一个非常强大且容易被忽略的“精确匹配”神器。它的作用是只有当指定的按键组合被精确按下时事件才会触发如果同时按下了其他系统修饰键则不会触发。为了让你彻底搞懂我们来看一个实际开发中最容易踩坑的场景 实际开发场景快捷键冲突假设你在做一个类似 Word 或编辑器的应用界面上有两个按钮普通保存用户按下 Ctrl S 时触发。强制保存用户按下 Ctrl Alt S 时触发。❌ 错误写法没有使用.exacttemplate!-- 普通保存 --buttonkeydown.ctrl.ssave保存/button!-- 强制保存 --buttonkeydown.ctrl.alt.sforceSave强制保存/button/template坑在哪里 当用户按下 Ctrl Alt S 时不仅“强制保存”会触发“普通保存”也会触发因为 Ctrl Alt S 确实包含了 Ctrl 和 SVue 默认是模糊匹配的。✅ 正确写法使用.exacttemplate!-- 只有精确按下 Ctrl S 才触发 --buttonkeydown.ctrl.s.exactsave保存/button!-- 只有精确按下 Ctrl Alt S 才触发 --buttonkeydown.ctrl.alt.s.exactforceSave强制保存/button/template加上.exact后Vue 会在底层额外检查除了你指定的修饰键其他所有的系统修饰键ctrl、alt、shift、meta必须处于未按下状态。这样两个快捷键就完美互不干扰了。如果你只想在用户单独按下 Ctrl 键点击时触发连 Shift 都不允许buttonclick.ctrl.exacthandleCtrlClick仅 Ctrl 点击/button如果你有一个按钮希望用户绝对不要带任何修饰键比如防止误触!-- 只有纯粹的单次点击才会触发 --buttonclick.exacthandlePureClick普通点击/button五、鼠标按键修饰符除了键盘Vue 3 还为我们提供了专门用来监听鼠标按键的修饰符。它们分别是.left鼠标左键主按键.right鼠标右键次按键.middle鼠标中键通常是滚轮键辅助按键实际开发场景自定义右键菜单需求在某个区域点击鼠标右键时阻止浏览器默认的右键菜单弹出并在鼠标点击的位置显示我们自定义的菜单。template!-- 1. contextmenu.prevent监听右键菜单事件并阻止浏览器默认菜单弹出 2. click.lefthideContextMenu当用户点击鼠标左键时隐藏自定义菜单 --divclasscontext-areacontextmenu.preventshowContextMenuclick.lefthideContextMenu在这里右键点击试试!-- 自定义菜单 --divv-ifmenuVisibleclasscustom-menu:style{ top: menuY px, left: menuX px }divclickhandleAction(复制)复制/divdivclickhandleAction(粘贴)粘贴/div/div/div/templatescriptsetupimport{ref}fromvue;constmenuVisibleref(false);constmenuXref(0);constmenuYref(0);// 显示自定义菜单constshowContextMenu(event){menuX.valueevent.clientX;menuY.valueevent.clientY;menuVisible.valuetrue;}// 左键点击时隐藏菜单consthideContextMenu(){menuVisible.valuefalse;}consthandleAction(action){console.log(执行了操作:,action);hideContextMenu();}/script六、事件汇总1、鼠标事件事件名称简写绑定示例触发时机clickclickhandleClick鼠标单击左键dblclickdblclickhandleDblClick鼠标双击左键contextmenucontextmenuhandleRightClick鼠标右键点击mousedownmousedownhandleDown鼠标按键被按下mouseupmouseuphandleUp鼠标按键被松开mouseentermouseenterhandleEnter鼠标移入元素及其子元素范围mouseleavemouseleavehandleLeave鼠标移出元素及其子元素范围mousemovemousemovehandleMove鼠标在元素内部移动wheelwheelhandleWheel鼠标滚轮滚动2、键盘事件事件名称简写绑定示例触发时机keydownkeydownhandleKeyDown键盘任意按键被按下keyupkeyuphandleKeyUp键盘任意按键被松开keypresskeypresshandleKeyPress键盘按键被按下并产生字符已废弃建议用 keydown3、表单事件事件名称简写绑定示例触发时机submitsubmit.preventhandleSubmit表单被提交时inputinputhandleInput输入框内容发生改变时changechangehandleChange输入框失去焦点且内容改变时或下拉框选项改变时focusfocushandleFocus输入框获取焦点时blurblurhandleBlur输入框失去焦点时避坑指南基于设备事件而非物理按键Vue 官方文档特别提到.left、.right实际上代表的是设备的“主”、“次”按键。如果用户是“左撇子”在系统里把鼠标左右键互换了那么物理上的右键点击依然会触发.left的事件。触控板也适用如果用户使用触控板单指点击触发.left双指点击触发.right三指点击触发.middle。区分右键点击的两种方式你可以使用click.right也可以使用contextmenu。在自定义菜单的场景中contextmenu配合.prevent是最标准的做法。七、事件触发顺序捕获阶段 (Capture Phase)事件从最外层如 window 对象开始逐层向下传播直到到达目标元素。目标阶段 (Target Phase)事件到达真正的目标元素event.target并在该元素上被触发。冒泡阶段 (Bubble Phase)事件从目标元素开始逐层向上传播直到最外层如 window 对象。默认情况下Vue 的事件监听器都是在“冒泡阶段”触发的。这意味着当你点击一个嵌套结构中的子元素时如果它的父元素、祖父元素也绑定了同类型的事件那么它们的事件处理器会从内向外依次触发。假设我们有这样的 HTML 结构divclickhandleDivClick外层 DivpclickhandlePClick内层 P 标签spanclickhandleSpanClick目标 Span/span/p/div当你点击最内层的span时Vue 事件的触发顺序是span的处理器 (handleSpanClick)p的处理器 (handlePClick)div的处理器 (handleDivClick)八、事件穿透场景一CSS 层面的事件穿透最常见通俗理解就像“隔山打牛”。页面上有一个透明的遮罩层比如弹窗的背景它盖在底下的按钮上。你明明点的是遮罩层但底下的按钮也跟着触发了。实际开发场景移动端经常遇到。你点了一个弹窗的“关闭”按钮弹窗瞬间消失但手指刚好碰到了弹窗底下原本位置的“删除”按钮结果把数据误删了。这就是经典的“点击穿透 Bug”。Vue 中的解决办法这其实不是 Vue 的问题而是 CSS 的问题。让事件穿透过去主动穿透给遮罩层加上pointer-events: none;。这样鼠标点击遮罩层时遮罩层会“隐身”事件直接作用于底下的元素。阻止事件穿透防止穿透给遮罩层加上click.stop阻止事件继续往下传。场景二DOM 层面的事件冒泡穿透通俗理解就像“水底冒泡”。当你点击一个嵌套在父元素里的子元素时子元素的事件触发了紧接着父元素上的同类事件也跟着触发了。实际开发场景你做了一个卡片列表点击整个卡片会跳转到详情页父元素事件但卡片右上角有个“删除”图标子元素。如果你点击删除图标却不小心触发了跳转详情页这就是事件冒泡穿透惹的祸。Vue 中的解决办法使用.stop修饰符。!-- 加上 .stop事件到此为止不再往上穿透/冒泡 --buttonclick.stophandleDelete删除/button