事件处理快速入门

Q: Vue3 中如何绑定事件?v-on 有哪些使用技巧?

A: 在 Vue 中绑定事件使用内置指令 v-on,指定事件类型和处理函数即可。使用方式非常直观,和原生 JS 的事件绑定思路一致,但多了很多贴心的增强功能。

<template>
  <div>{{ count }}</div>
  <button v-on:click="add">+1</button>
</template>

<script setup>
import { ref } from 'vue';
const count = ref(0);
function add() {
  count.value++;
}
</script>

事件处理的各种细节

1. 内联事件处理器

如果事件逻辑非常简单,可以将处理逻辑直接写在模板中:

<template>
  <div>{{ count }}</div>
  <button v-on:click="count++">+1</button>
</template>

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

这种方式仅适合极简单的逻辑,稍复杂的情况还是推荐使用独立的方法。

2. 简写形式

绑定事件是一个高频操作,Vue 提供了 @ 符号作为 v-on: 的简写:

<button @click="count++">+1</button>

在日常开发中,你看到的基本都是简写形式,前者基本只出现在教学示例中。

3. 向事件处理器传递参数

如果需要在触发事件时传入自定义参数,直接在模板中调用即可:

<template>
  <div>{{ count }}</div>
  <button @click="add('Hello World')">+1</button>
</template>

<script setup>
import { ref } from 'vue';
const count = ref(0);
function add(message) {
  count.value++;
  console.log(message);
}
</script>

4. 事件对象

这是一个比较容易踩坑的点。Vue 中事件对象的获取方式取决于是否传参

没有传参时,事件对象会自动作为第一个参数传入:

<template>
  <div>{{ count }}</div>
  <button @click="add">+1</button>
</template>

<script setup>
import { ref } from 'vue';
const count = ref(0);
// 事件对象自动传入,形参中声明即可
function add(event) {
  count.value++;
  console.log(event);
  console.log(event.target);
  console.log(event.clientX, event.clientY);
}
</script>

有传参时,需要通过特殊的 $event 变量显式传递事件对象:

<template>
  <div>{{ count }}</div>
  
  <button @click="add('Hello World', $event)">+1</button>
</template>

<script setup>
import { ref } from 'vue';
const count = ref(0);
function add(message, event) {
  count.value++;
  console.log(message);
  console.log(event);
  console.log(event.target);
  console.log(event.clientX, event.clientY);
}
</script>

如果是箭头函数,写法更加灵活,参数名不限于 $event

<template>
  <div>{{ count }}</div>
  <button @click="(event) => add('Hello World', event)">+1</button>
</template>

<script setup>
import { ref } from 'vue';
const count = ref(0);
function add(message, event) {
  count.value++;
  console.log(message);
  console.log(event);
}
</script>

最佳实践:简单场景用 $event,复杂场景用箭头函数包裹更灵活。


事件修饰符

Q: 什么是事件修饰符?为什么需要它们?

A: 在原生 JS 的事件处理中,我们经常需要在处理函数里写 event.preventDefault() 阻止默认行为,或者 event.stopPropagation() 阻止冒泡。这些「非业务逻辑」的代码和真正的业务逻辑混在一起,让代码不够纯粹。Vue 提供了事件修饰符来解决这个问题,让事件处理器只关注业务本身。

常见的事件修饰符:

修饰符 作用
.stop 阻止事件冒泡
.prevent 阻止默认行为
.self 只在元素本身触发(不在子元素上)
.capture 在捕获阶段触发(而非冒泡阶段)
.once 事件只触发一次
.passive 提升页面滚动性能

使用也非常简单,在事件名后通过 . 连接即可:

<button @click.stop="handleClick">点击我</button>

一个具体示例——只触发一次的事件:

<template>
  <button @click.once="clickHandle">click</button>
</template>

<script setup>
function clickHandle() {
  console.log('你触发了事件');
}
</script>

修饰符可以连用。例如,需要一个按钮同时满足:阻止冒泡、阻止默认行为、捕获阶段触发、只触发一次:

<button @click.capture.stop.prevent.once>click</button>

修饰符的顺序通常不影响最终行为,因为不同的修饰符控制不同方面的行为,相互不冲突。

按键修饰符

除了事件修饰符,Vue 还提供了一组按键修饰符,用于快速检查特定的按键:

  • .enter / .tab / .delete(捕获 Delete 和 Backspace)/ .esc / .space
  • .up / .down / .left / .right
  • .ctrl / .alt / .shift / .meta(不同系统对应不同按键)
<template>
  <input type="text" @keyup.enter="submitText" />
</template>

<script setup>
function submitText() {
  console.log('你要提交输入的内容');
}
</script>

按键修饰符也支持连用。例如,需要 Alt + Enter 才触发提交:

<input type="text" @keyup.alt.enter="submitText" />

Mac 系统中,.alt 对应的是 Option 按键。

.exact 精确修饰符

.exact 修饰符的作用是「精确匹配」——触发事件时,必须是指定的按键组合,不能有其他按键。


<button @click.ctrl="onClick">A</button>


<button @click.ctrl.exact="onClick">A</button>

鼠标按键修饰符

最后还有三个鼠标按键修饰符:.left.right.middle

<template>
  <button
    class="context-menu-button"
    @contextmenu.prevent.right="handleRightClick"
  >
    右键点击
  </button>
</template>

<script setup>
function handleRightClick() {
  console.log('你点击了鼠标右键');
}
</script>

上面的例子用到了修饰符连用——.prevent 阻止默认的右键菜单弹出,.right 指定仅响应鼠标右键。


表单双向绑定

Q: v-model 是怎么实现双向绑定的?它和手动绑定有什么区别?

A: 先看一个手动实现双向绑定的例子:

<template>
  <input
    type="text"
    :value="textContent"
    @input="(e) => (textContent = e.target.value)"
  />
  <p>你当前输入的内容为:{{ textContent }}</p>
</template>

<script setup>
import { ref } from 'vue';
const textContent = ref('');
</script>

上面的代码通过 :value 将数据绑定到文本框,再通过 @input 事件将用户输入同步回数据——这就是双向绑定的本质。但这样写太繁琐了,每种表单元素都要记住对应的事件和属性。于是 Vue 提供了 v-model,一行搞定:

<template>
  <input type="text" v-model="textContent" />
  <p>你当前输入的内容为:{{ textContent }}</p>
</template>

<script setup>
import { ref } from 'vue';
const textContent = ref('');
</script>

v-model 的好处不止于简化书写,更智能的是它能根据表单元素类型自动选择合适的事件和属性组合

表单元素 绑定事件 绑定属性
input / textarea input value
checkbox / radio change checked
select change value

各种表单元素的 v-model 用法

文本域(textarea):

<template>
  <textarea cols="30" rows="10" v-model="textContent"></textarea>
  <p>你当前输入的内容为:{{ textContent }}</p>
</template>

<script setup>
import { ref } from 'vue';
const textContent = ref('');
</script>

单个复选框——绑定布尔值,true 表示选中,false 表示未选中:

<template>
  <input type="checkbox" v-model="checked" />
  <button @click="checked = !checked">切换选中</button>
</template>

<script setup>
import { ref } from 'vue';
const checked = ref(true);
</script>

也可以通过 :true-value:false-value 自定义真假值:

<template>
  <input
    type="checkbox"
    v-model="checked"
    :true-value="customTrue"
    :false-value="customFalse"
  />
  <button @click="toggle">切换选中</button>
</template>

<script setup>
import { ref } from 'vue';
const checked = ref('yes');
// yes 表示选中,no 表示未选中
const customTrue = ref('yes');
const customFalse = ref('no');
function toggle() {
  checked.value === 'yes' ? (checked.value = 'no') : (checked.value = 'yes');
}
</script>

多个复选框——绑定到同一个数组,被选中的复选框的值会自动加入数组:

<template>
  <div v-for="(item, index) in arr" :key="index">
    <label :for="item.id">{{ item.title }}</label>
    <input type="checkbox" v-model="hobby" :id="item.id" :value="item.value" />
  </div>
  <p>{{ message }}</p>
</template>

<script setup>
import { ref, computed } from 'vue';
const hobby = ref([]);
const arr = ref([
  { id: 'swim', title: '游泳', value: '游个泳' },
  { id: 'run', title: '跑步', value: '跑个步' },
  { id: 'game', title: '游戏', value: '玩个游戏' },
  { id: 'music', title: '音乐', value: '听个音乐' },
  { id: 'movie', title: '电影', value: '看个电影' },
]);
const message = computed(() => {
  if (hobby.value.length === 0) return '请选择爱好';
  else return `您选择了${hobby.value.join('、')}`;
});
</script>

单选框(radio):

<template>
  <label for="male"></label>
  <input type="radio" id="male" v-model="gender" value="男" />
  <label for="female"></label>
  <input type="radio" id="female" v-model="gender" value="女" />
  <label for="secret">保密</label>
  <input type="radio" id="secret" v-model="gender" value="保密" />
</template>

<script setup>
import { ref } from 'vue';
const gender = ref('保密');
setTimeout(() => {
  gender.value = '男';
}, 3000);
</script>

哪个单选框被选中,取决于 gender 的值与哪个 value 匹配。

下拉列表(select)——v-model 写在 select 上,根据单选/多选绑定不同类型:

<template>
  
  <select v-model="hometown1">
    <option value="" disabled>请选择</option>
    <option
      v-for="(item, index) in hometownList"
      :key="index"
      :value="item.key"
    >
      {{ item.value }}
    </option>
  </select>
  <p>您选择的家乡为:{{ hometown1 }}</p>

  
  <select v-model="hometown2" multiple>
    <option value="" disabled>请选择</option>
    <option
      v-for="(item, index) in hometownList"
      :key="index"
      :value="item.key"
    >
      {{ item.value }}
    </option>
  </select>
  <p>您选择的家乡为:{{ hometown2 }}</p>
</template>

<script setup>
import { ref } from 'vue';
const hometown1 = ref('');
const hometown2 = ref([]);
const hometownList = ref([
  { key: '成都', value: '成都' },
  { key: '帝都', value: '北京' },
  { key: '魔都', value: '上海' },
  { key: '妖都', value: '广州' },
  { key: '陪都', value: '重庆' },
]);
</script>

关键点:单选绑定字符串,多选绑定数组

表单修饰符

Q: v-model 有哪些实用的修饰符?

A: Vue 为 v-model 提供了三个常用的修饰符:

修饰符 作用
.lazy 改由 change 事件触发更新(默认是 input
.number 自动将输入转为 number 类型
.trim 自动去除输入首尾空格
<template>
  
  <input type="text" v-model.lazy="mess1" />
  <p>你输入的是:{{ mess1 }}</p>

  
  <input type="text" v-model.number="mess2" />
  <p>你输入的是:{{ mess2 }}</p>
  <p>类型为{{ typeof mess2 }}</p>

  
  <input type="text" v-model.trim="mess3" />
  <p>你输入的是:{{ mess3 }}</p>
</template>

<script setup>
import { ref } from 'vue';
const mess1 = ref('');
const mess2 = ref('');
const mess3 = ref('');
</script>

这三个修饰符覆盖了表单开发中最常见的需求——同步时机控制、类型转换、空白处理,比手动在事件处理函数中写这些逻辑要简洁太多。


小结

本文覆盖了 Vue3 中事件处理与表单绑定的核心知识点:

  • 事件绑定v-on(简写 @)绑定事件,支持内联处理器、方法处理器、参数传递。事件对象在无参时自动传入,有参时通过 $event 或箭头函数传递。
  • 事件修饰符.stop.prevent.once.capture.self.passive 将非业务逻辑从处理函数中剥离,修饰符支持链式连用。
  • 按键修饰符.enter.alt.ctrl 等可快速检测按键组合,.exact 实现精确匹配。
  • v-model 双向绑定:自动根据表单元素类型选择正确的事件和属性组合。复选框(数组绑定多选)、单选框、下拉列表(单选字符串/多选数组)各有特定用法。
  • 表单修饰符.lazy 控制更新时机、.number 类型转换、.trim 去空格,覆盖常见表单处理需求。

掌握这些技能后,处理用户交互和表单将变得非常高效。