跳到主要内容

第 4 节:事件处理

📚 本节目标

  • 掌握 v-on 指令的使用方法
  • 学会处理点击、输入等常见事件
  • 理解事件处理函数的写法
  • 掌握常用事件修饰符(.stop、.prevent等)

一、什么是事件?

事件就是用户的操作,比如:

  • 点击按钮 → click 事件
  • 输入文字 → input 事件
  • 按下键盘 → keyup 事件
  • 鼠标移入 → mouseenter 事件

大白话:事件 = 用户做了某件事,我们要对这件事做出反应。


二、v-on 指令基础

基本语法

<!-- 完整写法 -->
<button v-on:click="函数名">点击我</button>

<!-- 简写(推荐) -->
<button @click="函数名">点击我</button>

记住v-on: 可以简写为 @(实际开发都用简写)


三、处理点击事件

示例 1:简单的计数器

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>点击计数器</title>
<style>
.counter {
text-align: center;
margin: 100px auto;
font-family: Arial;
}
.count {
font-size: 80px;
color: #42b983;
font-weight: bold;
}
button {
font-size: 20px;
padding: 15px 30px;
margin: 10px;
cursor: pointer;
border: none;
border-radius: 5px;
background: #42b983;
color: white;
}
button:hover {
background: #35a372;
}
</style>
</head>
<body>
<div id="app">
<div class="counter">
<h2>点击计数器</h2>

<!-- 显示计数 -->
<div class="count">{{ count }}</div>

<!-- 点击按钮,调用对应的函数 -->
<button @click="increment">增加 +1</button>
<button @click="decrement">减少 -1</button>
<button @click="reset">重置</button>
</div>
</div>

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
const { createApp } = Vue;

createApp({
data() {
return {
count: 0 // 计数器的初始值
}
},
// methods:定义事件处理函数的地方
methods: {
// 增加函数
increment() {
this.count++; // this 指向当前 Vue 实例
},
// 减少函数
decrement() {
this.count--;
},
// 重置函数
reset() {
this.count = 0;
}
}
}).mount('#app');
</script>
</body>
</html>

预期效果

  • 点击"增加",数字加 1
  • 点击"减少",数字减 1
  • 点击"重置",数字变回 0

关键点解释

  1. methods 选项:专门用来定义函数的地方
  2. this 关键字:在 methods 里,this 代表当前 Vue 实例,可以访问 data 中的数据
  3. 函数调用@click="increment" 点击时会自动执行 increment() 函数

示例 2:带参数的事件处理

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>带参数的事件</title>
<style>
.container {
max-width: 600px;
margin: 50px auto;
padding: 20px;
}
button {
padding: 10px 20px;
margin: 5px;
font-size: 16px;
}
.message {
margin-top: 20px;
padding: 15px;
background: #f0f0f0;
border-radius: 5px;
font-size: 18px;
}
</style>
</head>
<body>
<div id="app">
<div class="container">
<h2>打招呼</h2>

<!-- 传递不同的参数 -->
<button @click="sayHello('小明')">向小明问好</button>
<button @click="sayHello('小红')">向小红问好</button>
<button @click="sayHello('小刚')">向小刚问好</button>

<!-- 显示消息 -->
<div class="message" v-if="message">
{{ message }}
</div>
</div>
</div>

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
const { createApp } = Vue;

createApp({
data() {
return {
message: ''
}
},
methods: {
// 定义带参数的函数
sayHello(name) {
this.message = `你好,${name}!欢迎来到 Vue 世界!`;
}
}
}).mount('#app');
</script>
</body>
</html>

预期效果

  • 点击不同按钮,下方显示不同的问候语

四、其他常见事件

1. 输入事件(input)

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>输入事件示例</title>
</head>
<body>
<div id="app">
<h2>实时字数统计</h2>

<!-- 监听输入事件 -->
<textarea
@input="countWords"
v-model="text"
rows="5"
cols="50"
placeholder="在这里输入文字...">
</textarea>

<p>字数:{{ wordCount }}</p>
</div>

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
const { createApp } = Vue;

createApp({
data() {
return {
text: '',
wordCount: 0
}
},
methods: {
countWords() {
// 计算字数(去掉空格)
this.wordCount = this.text.replace(/\s/g, '').length;
}
}
}).mount('#app');
</script>
</body>
</html>

2. 鼠标事件(mouseenter、mouseleave)

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>鼠标事件示例</title>
<style>
.box {
width: 200px;
height: 200px;
background: #42b983;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 20px;
margin: 50px auto;
cursor: pointer;
transition: all 0.3s;
}
.box.active {
width: 250px;
height: 250px;
background: #ff6b6b;
}
</style>
</head>
<body>
<div id="app">
<div
class="box"
:class="{ active: isHovered }"
@mouseenter="handleMouseEnter"
@mouseleave="handleMouseLeave">
{{ isHovered ? '鼠标在里面!' : '把鼠标移过来' }}
</div>
</div>

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
const { createApp } = Vue;

createApp({
data() {
return {
isHovered: false
}
},
methods: {
handleMouseEnter() {
this.isHovered = true;
},
handleMouseLeave() {
this.isHovered = false;
}
}
}).mount('#app');
</script>
</body>
</html>

预期效果

  • 鼠标移入盒子:盒子变大、变红
  • 鼠标移出:恢复原状

五、获取事件对象

有时候我们需要获取事件的详细信息(比如按了哪个键、点击的坐标等)。

方式 1:默认传递事件对象

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>事件对象示例</title>
</head>
<body>
<div id="app">
<h2>按键检测</h2>

<!-- 不传参数时,Vue 会自动传递事件对象 -->
<input
type="text"
@keyup="showKey"
placeholder="按任意键试试">

<p v-if="lastKey">你按下的键是:{{ lastKey }}</p>
</div>

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
const { createApp } = Vue;

createApp({
data() {
return {
lastKey: ''
}
},
methods: {
// event 是事件对象
showKey(event) {
this.lastKey = event.key; // 获取按键名称
console.log('事件对象:', event);
}
}
}).mount('#app');
</script>
</body>
</html>

方式 2:既要参数,又要事件对象

<button @click="handleClick('参数', $event)">点击</button>

<script>
methods: {
handleClick(param, event) {
console.log('参数:', param);
console.log('事件对象:', event);
}
}
</script>

记住$event 是 Vue 提供的特殊变量,代表事件对象。


六、事件修饰符(重要)

什么是事件修饰符?

事件修饰符是 Vue 提供的快捷方式,用来处理一些常见的事件需求。

语法:在事件名后面加 .修饰符

@click.stop="函数名"
@submit.prevent="函数名"

常用修饰符

1. .prevent - 阻止默认行为

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>.prevent 示例</title>
</head>
<body>
<div id="app">
<h2>表单提交</h2>

<!-- 不加 .prevent:点击会刷新页面 -->
<form @submit="handleSubmit1">
<input type="text" placeholder="不加 .prevent">
<button>提交(会刷新页面)</button>
</form>

<hr>

<!-- 加了 .prevent:阻止默认的表单提交行为 -->
<form @submit.prevent="handleSubmit2">
<input type="text" v-model="username">
<button>提交(不会刷新)</button>
</form>

<p v-if="message">{{ message }}</p>
</div>

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
const { createApp } = Vue;

createApp({
data() {
return {
username: '',
message: ''
}
},
methods: {
handleSubmit1() {
alert('页面会刷新!');
},
handleSubmit2() {
this.message = `提交成功!用户名:${this.username}`;
}
}
}).mount('#app');
</script>
</body>
</html>

通俗解释

  • 表单默认行为:点击提交按钮会刷新页面
  • 加了 .prevent:阻止刷新,只执行我们的函数

2. .stop - 阻止事件冒泡

什么是事件冒泡

  • 点击子元素时,父元素的点击事件也会被触发
  • 就像水泡从水底往上冒一样
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>.stop 示例</title>
<style>
.parent {
padding: 50px;
background: lightblue;
}
.child {
padding: 30px;
background: lightcoral;
}
</style>
</head>
<body>
<div id="app">
<h2>事件冒泡演示</h2>

<!-- 不加 .stop:点击子元素,父元素也会触发 -->
<div class="parent" @click="parentClick">
父元素
<div class="child" @click="childClick">
子元素(点我,父元素也会触发)
</div>
</div>

<hr>

<!-- 加了 .stop:阻止冒泡 -->
<div class="parent" @click="parentClick">
父元素
<div class="child" @click.stop="childClick">
子元素(点我,父元素不会触发)
</div>
</div>
</div>

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
const { createApp } = Vue;

createApp({
methods: {
parentClick() {
alert('父元素被点击了!');
},
childClick() {
alert('子元素被点击了!');
}
}
}).mount('#app');
</script>
</body>
</html>

3. .once - 只触发一次

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>.once 示例</title>
</head>
<body>
<div id="app">
<!-- 普通点击:可以重复触发 -->
<button @click="count++">
普通点击:{{ count }}
</button>

<!-- 加了 .once:只能触发一次 -->
<button @click.once="showMessage">
只能点击一次
</button>
</div>

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
const { createApp } = Vue;

createApp({
data() {
return {
count: 0
}
},
methods: {
showMessage() {
alert('这个按钮只能点击一次!');
}
}
}).mount('#app');
</script>
</body>
</html>

4. 按键修饰符

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>按键修饰符示例</title>
</head>
<body>
<div id="app">
<h2>按键修饰符</h2>

<!-- 按 Enter 键触发 -->
<input
type="text"
v-model="message"
@keyup.enter="submit"
placeholder="输入后按 Enter">

<!-- 按 Esc 键触发 -->
<input
type="text"
@keyup.esc="clear"
placeholder="按 Esc 清空">

<p v-if="result">{{ result }}</p>
</div>

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
const { createApp } = Vue;

createApp({
data() {
return {
message: '',
result: ''
}
},
methods: {
submit() {
this.result = `你输入了:${this.message}`;
this.message = '';
},
clear() {
this.message = '';
this.result = '已清空!';
}
}
}).mount('#app');
</script>
</body>
</html>

常用按键修饰符

  • .enter - 回车键
  • .tab - Tab 键
  • .delete - 删除键
  • .esc - Esc 键
  • .space - 空格键

七、修饰符总结表

修饰符作用使用场景
.prevent阻止默认行为表单提交、链接跳转
.stop阻止事件冒泡嵌套元素的点击
.once只触发一次一次性操作(如领取优惠券)
.enter按下 Enter 键搜索框、输入确认
.esc按下 Esc 键关闭弹窗

八、综合练习:任务清单(简化版)

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>简易任务清单</title>
<style>
.container {
max-width: 500px;
margin: 50px auto;
padding: 20px;
}
.input-group {
display: flex;
margin-bottom: 20px;
}
.input-group input {
flex: 1;
padding: 10px;
font-size: 16px;
}
.input-group button {
padding: 10px 20px;
background: #42b983;
color: white;
border: none;
cursor: pointer;
}
.task-list {
list-style: none;
padding: 0;
}
.task-item {
padding: 15px;
background: #f0f0f0;
margin-bottom: 10px;
border-radius: 5px;
display: flex;
justify-content: space-between;
align-items: center;
}
.delete-btn {
background: #ff6b6b;
color: white;
border: none;
padding: 5px 15px;
border-radius: 3px;
cursor: pointer;
}
</style>
</head>
<body>
<div id="app">
<div class="container">
<h2>📝 我的任务清单</h2>

<!-- 输入框:按 Enter 或点击按钮添加任务 -->
<div class="input-group">
<input
type="text"
v-model="newTask"
@keyup.enter="addTask"
placeholder="输入任务,按 Enter 添加">
<button @click="addTask">添加</button>
</div>

<!-- 任务列表 -->
<ul class="task-list" v-if="tasks.length > 0">
<li class="task-item" v-for="(task, index) in tasks" :key="index">
<span>{{ task }}</span>
<button class="delete-btn" @click="deleteTask(index)">删除</button>
</li>
</ul>

<!-- 空状态提示 -->
<p v-else style="text-align: center; color: #999;">
还没有任务,快添加一个吧!
</p>

<!-- 统计 -->
<p>共有 {{ tasks.length }} 个任务</p>
</div>
</div>

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
const { createApp } = Vue;

createApp({
data() {
return {
newTask: '', // 新任务输入框的值
tasks: [] // 任务列表
}
},
methods: {
// 添加任务
addTask() {
// 如果输入框不为空
if (this.newTask.trim()) {
this.tasks.push(this.newTask);
this.newTask = ''; // 清空输入框
}
},
// 删除任务
deleteTask(index) {
this.tasks.splice(index, 1);
}
}
}).mount('#app');
</script>
</body>
</html>

预期效果

  • 输入任务,按 Enter 或点击"添加"按钮
  • 任务出现在列表中
  • 点击"删除"可以移除任务
  • 实时显示任务数量

九、本节重点回顾

v-on(简写 @) 用来监听事件
methods 选项定义事件处理函数
this 在 methods 中指向 Vue 实例
事件修饰符 简化常见操作(.prevent、.stop、.once 等)
按键修饰符 监听特定按键(.enter、.esc 等)


十、练习题

练习 1:改进计数器

为之前的计数器添加以下功能:

  1. 当计数小于 0 时,"减少"按钮变灰(禁用)
  2. 添加一个"加 10"按钮
  3. 添加一个"减 10"按钮
点击查看答案
<div id="app">
<div class="counter">
<div class="count">{{ count }}</div>
<button @click="increment">+1</button>
<button @click="count += 10">+10</button>
<button @click="decrement" :disabled="count <= 0">-1</button>
<button @click="count -= 10" :disabled="count < 10">-10</button>
<button @click="reset">重置</button>
</div>
</div>

<script>
const { createApp } = Vue;

createApp({
data() {
return {
count: 0
}
},
methods: {
increment() {
this.count++;
},
decrement() {
if (this.count > 0) {
this.count--;
}
},
reset() {
this.count = 0;
}
}
}).mount('#app');
</script>

练习 2:搜索框(按 Enter 搜索)

创建一个搜索框,要求:

  • 输入关键词后按 Enter 键触发搜索
  • 显示"正在搜索:XXX"
  • 添加一个清空按钮(按 Esc 也能清空)
点击查看答案
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>搜索框</title>
</head>
<body>
<div id="app">
<h2>🔍 搜索</h2>
<input
type="text"
v-model="keyword"
@keyup.enter="search"
@keyup.esc="clear"
placeholder="输入关键词,按 Enter 搜索,Esc 清空">
<button @click="search">搜索</button>
<button @click="clear">清空</button>

<p v-if="searchResult">{{ searchResult }}</p>
</div>

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
const { createApp } = Vue;

createApp({
data() {
return {
keyword: '',
searchResult: ''
}
},
methods: {
search() {
if (this.keyword.trim()) {
this.searchResult = `正在搜索:${this.keyword}`;
}
},
clear() {
this.keyword = '';
this.searchResult = '';
}
}
}).mount('#app');
</script>
</body>
</html>

🎉 恭喜你完成第四节课!

下一节我们将学习:条件渲染与列表渲染 —— 学会使用 v-if 控制元素显示/隐藏,用 v-for 循环渲染列表。