Vue.js事件处理全解析:核心语法、修饰符与实践应用
2.3.1 事件处理的核心语法
2.3.1.1 事件处理知识点
在Vue.js开发中,事件处理是构建交互性页面的基础。理解其核心语法对于开发者至关重要。
指令的语法格式
在Vue中,指令是一种特殊的HTML属性,用于将DOM元素与Vue实例的数据和行为进行绑定。指令的基本语法格式为:<标签 v-指令名:参数名="表达式">{{插值语法}}</标签>
。这里的“表达式”位置灵活性极高,它可以是常量,比如直接写一个数字或字符串;也可以是JavaScript表达式,像简单的数学运算2 + 3
或逻辑判断 a > 10
等;还可以是Vue实例所管理的数据属性。例如,假设Vue实例的 data
选项中有一个属性 message
,那么在表达式中就可以直接使用 message
。
在Vue当中完成事件绑定需要哪个指令呢
Vue通过 v-on
指令来实现事件绑定。其语法格式为 v-on:事件名="表达式"
。例如,v-on:click="表达式"
意味着当元素发生鼠标单击事件时,会执行其后的表达式。同样,v-on:keydown="表达式"
则是在键盘按下事件发生后执行表达式。
在实际应用中,事件绑定常常用于触发函数调用。比如,我们有一个按钮,希望在点击它时调用一个函数来执行某些操作。在Vue中,所有事件所关联的回调函数,都需要在Vue实例的配置项 methods
中进行定义。methods
是一个对象,在这个对象中可以定义多个回调函数。例如:
const vm = new Vue({
el: "#app",
data: {
num: 1
},
methods: {
handleClick() {
// 这里编写按钮点击后的处理逻辑
console.log('按钮被点击了');
}
}
});
然后在HTML模板中,可以这样绑定事件:<button v-on:click="handleClick">点击我</button>
v-on指令也有简写形式
为了提高代码编写的效率和简洁性,v-on
指令有其简写形式。例如,v-on:click
可以简写为 @click
,v-on:keydown
简写为 @keydown
,v-on:mouseover
简写为 @mouseover
等等。这样在模板中书写事件绑定时更加便捷,如 <button @click="handleClick">点击我</button>
。
绑定的回调函数,如果函数调用时不需要传递任何参数,小括号()可以省略。默认传递事件对象event
当绑定的回调函数不需要传递额外参数时,函数调用的小括号 ()
可以省略。此时,Vue会默认将当前发生的事件对象 event
传递给回调函数。例如:
<button @click="handleClick">按钮</button>
methods: {
handleClick(event) {
console.log(event); // 这里可以访问到事件对象
}
}
Vue在调用回调函数的时候,会自动给回调函数传递一个对象,这个对象是:当前发生的事件对象
Vue在事件触发并调用回调函数时,会自动将当前发生的事件对象作为参数传递给回调函数。这个事件对象包含了与事件相关的各种信息,如鼠标事件中的鼠标位置、键盘事件中的按键信息等。通过访问这个事件对象,开发者可以实现更丰富的交互逻辑。
在绑定回调函数的时候,可以在回调函数的参数上使用 e v e n t 占位符, V u e 框架看到这个 event占位符,Vue框架看到这个 event占位符,Vue框架看到这个event占位符之后,会自动将当前事件以对象的形式传过去
在某些情况下,我们可能希望在传递自定义参数的同时,也能获取到事件对象。这时可以在回调函数的参数中使用 $event
占位符。例如:
<button @click="handleClick($event, 'customParam')">按钮</button>
methods: {
handleClick(event, param) {
console.log(event); // 事件对象
console.log(param); // 自定义参数 'customParam'
}
}
下面通过一个完整的示例来展示这些知识点的综合应用:
<body>
<!-- 容器 -->
<div id="app">
<h1>{{msg}}</h1>
<!--需求一: 使用javascript原生代码实现,点击弹出‘hello提示’。 -->
<button onclick="alert('hello')">按钮1</button>
<!--需求二: 使用Vue来完成事件绑定 -->
<!-- 1、注意:alert()并没有被Vue实例管理,无法直接调用-->
<!-- <button v-on:click="alert('hello')">按钮2</button> -->
<!-- 2、注意:全局定义的sayHello()也不会被Vue实例管理。 -->
<!-- <button v-on:click="sayHello()">按钮3</button> -->
<!-- 3、正确的写法,配合methods配置项 -->
<button v-on:click="sayHello()">按钮4</button>
<!-- 4、v-on指令的简写形式 -->
<button @click="sayHi()">按钮5</button>
<!-- 5、v-on指令的传参 sayHi($event, 'jack') -->
<button @click="sayHi($event, 'jack')">按钮6</button>
<!-- 6、 绑定的回调函数,如果不需要传任何参数,小括号() 可以省略 -->
<button @click="sayWhat">按钮7</button>
</div>
<!-- vue代码 -->
<script>
// 自定义一个函数
// function sayHello(){
// alert('hello')
// }
const vm = new Vue({
el: "#app",
data: {
num: 1,
},
methods: {
//函数的完整写法
// sayHello:function {
// alert("hello");
// },
// 回调函数
sayHello() {
alert("hello");
},
sayHi(event, name) {
console.log(name, event);
//alert("hi " + name)
},
sayWhat(event) {
// console.log(event)
// console.log(event.target)
// console.log(event.target.innerText)
// alert('hello')
},
},
});
</script>
</body>
在这个示例中,我们展示了如何使用Vue进行事件绑定,包括常规的 v-on
语法、简写形式、传递参数以及省略小括号的情况。
2.3.1.2 事件回调函数中的this
在Vue的事件回调函数中,this
的指向有着特殊的规则,这对于正确操作Vue实例的数据和方法非常关键。
常规写法下:回调函数中的this是vm
当使用常规函数定义事件回调函数时,函数内部的 this
指向Vue实例(即 vm
)。这意味着在回调函数中可以直接访问Vue实例的 data
数据和 methods
中的其他方法。例如:
<body>
<!-- 容器 -->
<div id="app">
<h1>{{msg}}</h1>
<h1>计数器:{{num}}</h1>
<!--在模版中,可以拿到num,对num++,数据改变了,就会改变页面 -->
<button @click="num++">点击我加1</button>
<!-- 方法的实现 -->
<button @click="add">点击我加2</button>
<button @click="add2">点击我加3(箭头函数)</button>
</div>
<!-- vue代码 -->
<script>
const vm = new Vue({
el: "#app",
data: {
msg: "关于事件回调函数中的this",
num: 0,
},
methods: {
add() {
//num+=2; // 错误的。
// 在这里需要操作num变量?怎么办?
//console.log(vm === this)
// console.log(this)
this.num += 2;
// vm.num+=3;
},
add2: () => {
//this.num+=3;
//console.log(this === vm)
//箭头函数中没有this,箭头函数中的this是从父级作用域当中继承过来的。
//对于当前程序来说,父级作用域是全局作用域:window
console.log(this);
},
//控制台通过vm直接调用
sayhello() {
alert("hello");
},
},
});
</script>
</body>
在上述代码中,add
方法是常规函数定义,在这个方法中 this.num += 2
能够正确地操作Vue实例中的 num
数据,因为 this
指向了 vm
。
箭头函数写法下:回调函数中的this是window
当使用箭头函数定义事件回调函数时,情况有所不同。箭头函数没有自己的 this
,它的 this
是继承过来的,默认这个 this
是箭头函数所在的宿主对象。由于对象不能构成单独的作用域,所以这个父级作用域是全局作用域,也就是 window
。这就导致在箭头函数定义的事件回调中,无法直接通过 this
访问Vue实例的 data
和 methods
。例如在上面代码中的 add2
方法,使用箭头函数定义,此时 this.num += 3
并不能正确操作Vue实例中的 num
数据,因为 this
指向的是 window
,而不是 vm
。
可以在函数中改变data中的数据,例如:this.num++,这样会联动页面上产生动态效果
在Vue中,通过事件回调函数改变 data
中的数据是实现页面动态交互的重要方式。当 data
中的数据发生变化时,Vue会自动检测到这种变化,并更新与之绑定的DOM元素,从而实现页面的动态更新。例如在前面的计数器示例中,点击按钮触发 add
方法,this.num += 2
改变了 num
的值,页面上显示的计数器数值也会随之更新。
回调函数并没有在vm对象上,为什么通过vm可以直接调用函数呢?尝试手写Vue框架
在Vue中,我们可以通过 vm
实例直接调用定义在 methods
中的回调函数,这背后的原理是Vue在实例化过程中进行了特殊处理。我们可以尝试手写一个简单的Vue框架来理解这个过程。
// 定义一个Vue类
class MyVue {
// 定义构造函数
// options 是一个对象{}
constructor(options) {
// 源码实现methods 目的希望vm可以直接调用方法
Object.keys(options.methods).forEach((methodName, index) => {
// 给当前的vue实例拓展一个方法
this[methodName] = options.methods[methodName]
});
}
}
在这个简单的实现中,通过遍历 options.methods
对象,将其中定义的方法逐一添加到Vue实例 this
上,这样就实现了通过 vm
实例直接调用这些方法。下面是一个使用这个自定义 MyVue
类的示例:
<head>
<meta charset="UTF-8" />
<title>methods实现原理</title>
<!-- 引入我们自己的vue.js -->
<script src="../js/myvue.js"></script>
</head>
<body>
<!-- vue程序 -->
<script>
const vm = new MyVue({
data: {
msg: "hello vue!",
},
methods: {
sayHi() {
//console.log(this === vm)
// console.log(this.msg);
console.log("hi");
},
sayHello: () => {
//console.log(this === vm)
console.log("hello");
},
},
});
</script>
</body>
在这个示例中,通过 new MyVue
创建的 vm
实例可以直接调用 sayHi
和 sayHello
方法,尽管它们最初定义在 methods
对象中,这就是通过上述机制实现的。
2.3.2 事件修饰符
在Vue.js中,方法通常只关注纯粹的数据逻辑,而不希望去处理DOM事件的细节。为了解决这个问题,Vue.js为 v-on
提供了事件修饰符。事件修饰符通过点开头的指令后缀来表示,它可以帮助我们更方便地处理常见的DOM事件行为。
.prevent:等同于event.preventDefault()阻止事件的默认行为
.prevent
修饰符用于阻止事件的默认行为。例如,在HTML中,<a>
标签的默认行为是在点击时跳转到指定的链接。如果我们希望阻止这种跳转,可以使用 .prevent
修饰符。例如:
<a href="https://www.baidu.com" @click.prevent="fun">百度</a>
在这个例子中,当点击“百度”链接时,不会跳转到百度页面,而是执行 fun
方法。如果 fun
方法中没有额外的阻止默认行为的代码,仅通过 .prevent
修饰符就实现了阻止链接跳转的功能。这在很多场景中非常有用,比如表单提交时,如果需要在前端进行一些验证,而不是直接提交表单,就可以使用 .prevent
修饰符来阻止表单的默认提交行为,然后在验证通过后再通过JavaScript代码手动提交表单。
.stop:停止事件冒泡,等同于event.stopPropagation()
事件冒泡是指当一个元素上的事件被触发时,该事件会向上传播到父元素,依次类推,直到DOM树的根节点。.stop
修饰符用于停止事件冒泡。例如:
<div @click="san">
<div @click="er">
<button @click="fun">事件冒泡</button>
</div>
</div>
在这个结构中,如果没有 .stop
修饰符,当点击按钮时,fun
方法会被调用,然后由于事件冒泡,er
方法和 san
方法也会依次被调用。但如果在按钮的点击事件绑定中添加 .stop
修饰符,即 <button @click.stop="fun">事件冒泡</button>
,那么当点击按钮时,只会调用 fun
方法,事件不会再向上冒泡到父元素,er
方法和 san
方法不会被触发。这在需要控制事件传播范围的场景中非常实用,比如在一个复杂的页面布局中,某个元素的点击事件不希望影响到其上层元素的相关事件处理。
.capture:添加事件监听器时使用事件捕获模式
添加事件监听器有两种不同的方式:一种是从内到外添加,这是事件冒泡模式;另一种是从外到内添加,即事件捕获模式。.capture
修饰符用于指定使用事件捕获模式添加事件监听器。例如:
<div @click.capture="san">
<div @click="er">
<button @click="fun">添加事件监听器的时候采用事件捕获模式</button>
</div>
</div>
在这个例子中,当点击按钮时,首先会触发 san
方法(因为使用了 .capture
修饰符,事件捕获从外到内),然后才会触发 er
方法和 fun
方法(按照正常的事件冒泡顺序)。事件捕获模式在一些特殊场景下很有用,比如需要在事件到达目标元素之前先进行一些全局的处理或拦截。
.self:这个事件如果是“我自己元素”上发生的事件,这个事件不是别人给我传递过来的事件,则执行对应的程序
.self
修饰符确保只有当事件在绑定的元素自身上触发时,才会执行对应的程序。例如:
<div @click="san">
<div @click.self="er">
<button @click="fun">self修饰符</button>
</div>
</div>
在这个结构中,如果点击按钮,fun
方法会被调用。但由于内部的 div
使用了 .self
修饰符,当点击内部 div
时,只有当点击的是内部 div
本身(而不是其内部的按钮等子元素)时,er
方法才会被调用。如果点击按钮,虽然按钮在内部 div
内,但事件是由按钮触发的,不是内部 div
自身触发,所以 er
方法不会被调用。这在需要区分事件是由元素自身还是其子元素触发的场景中非常有用。
.once:事件只发生一次
.once
修饰符使得事件只发生一次。例如:
<button @click.once="fun">事件只发生一次</button>
当点击这个按钮时,fun
方法只会被调用一次。后续再次点击按钮,fun
方法不会再被触发。这在一些只需要执行一次的初始化操作或一次性交互场景中很有用,比如引导用户完成某个首次操作的提示,只需要显示一次。
.passive:passive翻译为顺从/不抵抗。无需等待,直接继续(立即)执行事件的默认行为
.passive
修饰符表示无需等待,直接继续(立即)执行事件的默认行为。例如,在一个可滚动的 div
元素上,如果绑定了 wheel
事件,默认情况下,当滚动鼠标滚轮时,浏览器会等待 wheel
事件的处理函数执行完后再决定是否执行滚动的默认行为。如果处理函数执行时间较长,会导致滚动卡顿。使用 .passive
修饰符可以避免这种卡顿,让浏览器直接执行滚动的默认行为,而不需要等待事件处理函数。
<div class="divList" @wheel.passive="testPassive">
<div class="item">div1</div>
<div class="item">div2</div>
<div class="item">div3</div>
</div>
需要注意的是,.prevent
用于阻止事件的默认行为,而 .passive
用于解除阻止,这两种修饰符是对立的,不可以共存。如果一起使用,会导致报错。
事件修饰符的联合使用
在Vue当中,事件修饰符是可以多个联合使用的。但是需要注意顺序,不同的顺序会导致不同的执行效果。例如:
<div @click="san">
<div @click.self.stop="er">
<button @click="fun">.self+.stop</button>
</div>
</div>
这里的 @click.self.stop
表示先判断事件是否是在内部 div
自身触发(.self
),如果是,则停止事件冒泡(.stop
)。如果写成 @click.stop.self
,则先停止事件冒泡,再判断是否是自身触发,这可能会导致与预期不同的结果。
以下是一个完整展示事件修饰符使用的示例代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>事件修饰符</title>
<!-- 安装Vue -->
<script src="../js/vue.js"></script>
<style>
div:not(#app) {
background-color: pink;
padding: 10px;
margin: 5px;
}
.divList {
width: 300px;
height: 200px;
background-color: aquamarine;
overflow: auto;
}
.item {
width: 300px;
height: 200px;
}
</style>
</head>
<body>
<!-- 容器 -->
<div id="app">
<h1>{{msg}}</h1>
<!--1、 阻止事件的默认行为 .prevent -->
<a href="https://www.baidu.com" @click.prevent="fun">百度</a>
<hr>
<!--2、 停止事件冒泡 .stop-->
<div @click="san">
<div @click="er">
<button @click="fun">事件冒泡</button>
</div>
</div>
<hr>
<!--3、 添加事件监听器时使用事件捕获模式 .capture -->
<!-- 默认采用冒泡模式。 -->
<div @click.capture="san">
<div @click="er">
<button @click="fun">添加事件监听器的时候采用事件捕获模式</button>
</div>
</div>
<hr>
<!--4、 .self修饰符 只有点击到自己的时候会运行-->
<div @click="san">
<div @click.self="er">
<button @click="fun">self修饰符</button>
</div>
</div>
<hr>
<!-- 在Vue当中,事件修饰符是可以多个联合使用的。
但是需要注意:
@click.self.stop:先.self,再.stop
@click.stop.self:先.stop,再.self
-->
<div @click="san">
<div @click.self.stop="er">
<button @click="fun">.self+.stop</button>
</div>
</div>
<hr>
<!-- 5、.once修饰符:事件只发生一次 -->
<button @click.once="fun">事件只发生一次</button>
<!-- 6、.passive修饰符 -->
<div class="divList" @wheel.passive="testPassive">
<div class="item">div1</div>
<div class="item">div2</div>
<div class="item">div3</div>
</div>
</div>
<!-- vue代码 -->
<script>
const vm = new Vue({
el: "#app",
data: {
msg: "事件修饰符",
},
methods: {
fun(event) {
// 手动调用事件对象的preventDefault()方法,可以阻止事件的默认行为。
//event.preventDefault();
alert("1");
},
er() {
alert(2);
},
san() {
alert(3);
},
testPassive(event) {
for (let i = 0; i < 100000; i++) {
console.log("test passive");
}
// 阻止事件的默认行为
//event.preventDefault()
},
},
});
</script>
</body>
</html>
2.3.3 按键修饰符
9个比较常用的按键修饰符
在Vue中,按键修饰符可以方便地处理键盘事件。以下是9个比较常用的按键修饰符:
.enter
:用于监听回车键事件。例如:<input type="text" @keyup.enter="getInfo" />
,当在输入框中按下回车键时,会触发getInfo
方法。.tab
:必须配合keydown
事件使用。例如:<input type="text" @keydown.tab="getInfo" />
,当按下Tab
键时触发相应方法。.delete
:捕获“删除”和“退格”键。例如:<input type="text" @keyup.delete="getInfo" />
,按下删除或退格键时执行getInfo
方法。.esc
:监听Esc
键。例如:<input type="text" @keyup.esc="getInfo" />
,按下Esc
键时触发方法。.space
:监听空格键。例如:<input type="text" @keyup.space="getInfo" />
,按下空格键时执行相应操作。.up
:监听向上箭头键。例如:<input type="text" @keyup.up="getInfo" />
,按下向上箭头键时触发方法。.down
:监听向下箭头键。例如:<input type="text" @keyup.down="getInfo" />
,按下向下箭头键时执行操作。.left
:监听向左箭头键。例如:<input type="text" @keyup.left="getInfo" />
,按下向左箭头键时触发方法。.right
:监听向右箭头键。例如:<input type="text" @keyup.right="getInfo" />
,按下向右箭头键时执行操作。
另外,也可以使用键值来监听特定按键。例如,回车键的键值是 13
,可以这样写:<input type="text" @keyup.13="getInfo" />
。不过需要注意的是,数字不具有通用性,不同的键盘排序可能会导致键值不同,所以更推荐使用按键修饰符。
怎么获取某个键的按键修饰符
获取某个键的按键修饰符可以通过以下两步实现:
第一步:通过 event.key
获取这个键的真实名字。例如,在事件处理函数中:
methods: {
handleKey(event) {
const keyName = event.key;
console.log(keyName);
}
}
第二步:将这个真实名字以 kebab-case
风格进行命名。例如,如果 event.key
返回的是 PageDown
,经过命名之后就是 page-down
。那么在模板中就可以这样使用:<input type="text" @keyup.page-down="getInfo" />
按键修饰符是可以自定义的
按键修饰符可以通过Vue的全局配置对象 config
进行自定义。步骤如下:
第一步:获取按键的键值 :event.keyCode
(不过需要注意的是,keyCode
在现代浏览器中已经逐渐被弃用,建议使用 event.key
或 event.code
)。
第二步:通过Vue的全局配置对象 config
来进行按键修饰符的自定义。语法规则为:Vue.config.keyCodes.按键修饰符的名字 = 键值
。例如:
Vue.config.keyCodes.huiche = 13;
然后在模板中就可以使用自定义的按键修饰符:<input type="text" @keyup.huiche="getInfo" />
系统修饰键
有4个比较特殊的系统修饰键:ctrl
、alt
、shift
、meta
。
对于 keydown
事件来说:只要按下 ctrl
键(以 ctrl
为例),keydown
事件就会触发。例如:<input type="text" @keydown.ctrl="getInfo" />
,当按下 Ctrl
键时,就会触发 getInfo
方法。
对于 keyup
事件来说:需要按下 ctrl
键,并且加上按下组合键,然后松开组合键之后,keyup
事件才能触发。例如:<input type="text" @keyup.ctrl="getInfo" />
,需要先按下 Ctrl
键,再按下其他键,然后松开所有键,才会触发 getInfo
方法。还可以指定具体的组合键,如 <input type="text" @keyup.ctrl.i="getInfo" />
,表示只有同时按下 Ctrl
和 I
键,然后松开,才会触发 getInfo
方法。
以下是一个综合展示按键修饰符使用的示例代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>按键修饰符</title>
<script src="../js/vue.js"></script>
</head>
<body>
<div id="app">
<!-- 回车键 -->
<input type="text" @keyup.enter="getInfo" />
<hr>
<!-- 回车键(键值) -->
<input type="text" @keyup.13="getInfo" />
<hr>
<!-- delete键 -->
<input type="text" @keyup.delete="getInfo" />
<hr>
<!-- esc键 -->
<input type="text" @keyup.esc="getInfo" />
<hr>
<!-- space键 -->
<input type="text" @keyup.space="getInfo" />
<hr>
<!-- up键 -->
<input type="text" @keyup.up="getInfo" />
<hr>
<!-- down键 -->
<input type="text" @keyup.down="getInfo" />
<hr>
<!-- left键 -->
<input type="text" @keyup.left="getInfo" />
<hr>
<!-- right键 -->
<input type="text" @keyup.right="getInfo" />
<hr>
<!-- PageDown键 -->
<input type="text" @keyup.page-down="getInfo" />
<hr>
<!-- 自定义按键修饰符 -->
<input type="text" @keyup.huiche="getInfo" />
<hr>
<!-- 系统修饰键: ctrl、alt、shift、meta -->
<input type="text" @keydown.ctrl="getInfo" />
<hr>
<input type="text" @keyup.ctrl="getInfo" />
<hr>
<input type="text" @keyup.ctrl.i="getInfo" />
<hr>
</div>
<script>
// 自定义按键修饰符
Vue.config.keyCodes.huiche = 13;
const vm = new Vue({
el: "#app",
methods: {
getInfo() {
console.log('按键事件触发');
}
}
});
</script>
</body>
</html>