第 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
关键点解释:
- methods 选项:专门用来定义函数的地方
- this 关键字:在 methods 里,
this代表当前 Vue 实例,可以访问 data 中的数据 - 函数调用:
@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:改进计数器
为之前的计数器添加以下功能:
- 当计数小于 0 时,"减少"按钮变灰(禁用)
- 添加一个"加 10"按钮
- 添加一个"减 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 循环渲染列表。