hello,我是小索奇,精心制作的Vue系列教程持续更新哈,想要学习&巩固&避坑就一起学习吧~
事件处理
事件的基本用法
重点内容
- 使用
v-on:xxx
缩写@xxx
绑定事件,其中xxx
是事件名(回顾:v-bind缩写为冒号:) - 事件的回调需要配置在
methods
对象中,最终会在VM
上 methods
中配置的函数,不要用箭头函数,否则 this 就不是VM了,重点强调!methods
中配置的函数,都是被 Vue所管理的函数,this 的指向是VM(ViewModel-vue实例对象)或组件实例对象@click="demo"
和@click="demo($event)"
效果一致,前者是默认会传event,省略了而已,后者更加灵活,可以传更多参数- 当我们需要给函数传参时
@click='showInfo(666)'
怎么获取event
? - 关于
this
的详细理解
详细解析
先看这一组代码,哪里有问题
<body>
<div id="root">
<h2>这是name:{{name}}</h2>
<button v-on:click = "showInfo">点击提示信息</button>
</div>
<script type="text/JS">
Vue.config.productionTip = false
function showInfo(){
alert('你好哈')
}
new Vue({
el:'#root',
data:{
name:'即兴小索奇'
}
})
</script>
</body>
不知道methods的伙伴可能看不出什么问题,但实际的问题是点击按钮没有任何反应
image-20230812205755903
这里定义了一个普通的JS 函数 showInfo()
,但没有将它添加到 Vue 实例的 methods
选项中,因此在模板中使用 v-on:click="showInfo"
时无法正确调用该函数我们需要将 showInfo
函数添加到 Vue 实例的 methods
选项中
因为 Vue 实例的 methods
选项专门用于存放可以在模板中调用的方法
当在methods
中加上showInfo
方法就会成功显示
image-20230812211754411
事件对象
当你在模板中使用事件处理函数时,Vue 会自动将事件对象作为第一个参数传递给该函数因此,你可以在事件处理函数中定义一个参数来接收这个事件对象常见的做法是将这个参数命名为 event
,以表示它是一个事件对象
例如,在你的代码中,如果你使用 v-on:click="showInfo"
来绑定事件,那么在 showInfo
函数中,你可以定义一个名为 event
的参数,这样就可以访问到事件对象,从而获取事件的信息(如点击的坐标、触发的元素等)
举个栗子
<body>
<div id="root">
<button v-on:click="showInfo">点击提示信息</button>
</div>
<script type="text/JS">
new Vue({
el: '#root',
data: {
name: '即兴小索奇'
},
methods: {
showInfo(event) {
console.log(event)
console.log(event.target)
alert('你好哈,点击坐标:' + event.clientX + ',' + event.clientY + ' ' + event.target.innerText);
}
}
});
</script>
</body>
image-20230812213221250
在这里这个事件就是button
event.target
就是这个事件目标(button)
image-20230812213746135
用console.log(event)
输出可以看到所有属性及方法
image-20230812213517353
当我们需要给函数传参时@click='showInfo(666)'
怎么获取event
?
当我们需要传参时,会发现event
不能输出了,即使设置多个参数也是undefined,这时候我们只要在参数后面加上$(event)
关键词即可
@click='showInfo(666,$event)
方法中对应两个参数就可以输出event了,这是官方设置的,记住就行- 对于event的顺序没有要求,可以把其它参数放在
event
后面也行
this函数的区分
如果把下面代码中的普通函数改为箭头函数,它的this会如何变化?
<body>
<div id="root">
<button v-on:click="showInfo">点击提示信息</button>
</div>
<script type="text/JS">
new Vue({
el: '#root',
data: {
name: '即兴小索奇'
},
methods: {
showInfo:(event)=> {
console.log(this) // 箭头函数时输出window
console.log(this) // 普通函数时输出vue
alert('你好哈,点击坐标:' + event.clientX + ',' + event.clientY + ' ' + event.target.innerText);
}
}
});
</script>
</body>
- 当showInfo为普通函数时,显示的是Vue对象
- 当showInfo为箭头函数时,显示的是Window对象
在普通函数中,this
的值是在函数被调用时确定的,它可以根据调用的方式(比如函数被作为对象的方法调用、作为构造函数调用等)而发生改变
而在箭头函数中,this
的值是由外围(定义箭头函数的上下文)的上下文确定的,它继承自最近的非箭头函数父级换句话说,箭头函数的 this
始终指向定义箭头函数的代码块所在的上下文,而不是调用箭头函数的方式
这种行为对于在回调函数中捕获外部上下文非常有用,但也可能导致在某些情况下出现意外的结果所以,大家在使用箭头函数时,需要特别注意 this
的行为哈
注意
- 在被Vue管理的函数中(在Vue实例对象里面)最好都写成普通函数,不要写箭头函数!
让我们首先来看看普通函数中的 this
:
函数作为独立函数调用: 当函数作为独立的函数调用时,this
会指向全局对象(在浏览器中通常是 window
对象,Node.js 中是 global
对象)
function normalFunction() {
console.log(this); // 在浏览器中输出 window(全局对象)
}
normalFunction();
函数作为对象方法调用: 当函数作为对象的方法调用时,this
会指向调用该方法的对象
const obj = {
name: 'John',
greet: function() {
console.log(this.name);
}
};
obj.greet(); // 输出 "John"
函数作为构造函数调用: 当函数用作构造函数创建新对象时,this
会指向正在创建的新对象
function Person(name) {
this.name = name;
}
const person = new Person('Alice');
console.log(person.name); // 输出 "Alice"
而在箭头函数中,this
的行为有所不同:
箭头函数中的 this
: 箭头函数的 this
始终由外围(定义箭头函数的上下文)的上下文决定,它不会因为函数被调用的方式而改变
const arrowFunction = () => {
console.log(this); // 这里的 this 是外围上下文中的 this
};
arrowFunction(); // 输出的 this 取决于外围上下文
这种行为使得箭头函数在某些情况下非常有用,例如在回调函数中,你可以捕获外部函数的上下文,避免了在回调中使用 that
或 self
来保存上下文
总结来说,普通函数中的 this
在调用时会根据调用方式和上下文而变化,而箭头函数中的 this
始终继承自外围上下文,不会随着调用方式的改变而改变
拓展一下
在箭头函数中,this
的取值是由箭头函数所在代码块的上下文决定的换句话说,箭头函数的 this
继承自最近的非箭头函数父级的 this
值
这意味着,如果箭头函数直接位于全局作用域中,那么它的 this
就会继承自全局对象(例如,在浏览器环境中,就是 window
对象)
让我们来看一个示例:
const arrowFunction = () => {
console.log(this);
};
arrowFunction(); // 输出的 this 取决于全局上下文,通常是 window 或 global 对象(取决于环境)
在浏览器环境中运行上述代码,arrowFunction
的 this
将指向全局对象 window
然而,如果箭头函数嵌套在其他函数或对象方法中,它的 this
将继承自外围上下文:
function outerFunction() {
const innerArrow = () => {
console.log(this);
};
innerArrow();
}
outerFunction(); // 输出的 this 取决于 outerFunction 的上下文
在这个示例中,innerArrow
的 this
将继承自 outerFunction
的上下文
我们可以在不同的上下文中尝试运行这些示例代码,并查看输出的 this
值,以更好地理解箭头函数的 this
行为
这里的this也就是输出全局对象window
<script type="text/JS">
new Vue({
el: '#root',
data: {
name: '小索奇'
},
methods: {
showInfo: () => {
console.log(this); // 输出全局对象 window,因为箭头函数没有外围上下文
}
}
});
</script>
索奇问答
这里可能又会有人问了
A:什么是外围上下文?
Q:在JS 中,每个函数都有一个自己的执行上下文(execution context),其中包含函数的作用域、参数、变量等信息函数的执行上下文在函数被调用时创建,随后被推入执行上下文栈(execution context stack)中,函数执行完毕后从栈中弹出
代码举例
假设有一个函数 outer
包含一个函数 inner
,在 inner
函数中,outer
就是 inner
函数的外围上下文
function outer() {
// 我是外围
const outerVar = 'I am outer';
function inner() {
console.log(outerVar); // 访问外围上下文中的变量
}
inner();
}
outer(); // 输出 "I am outer"
在这个例子中,inner
函数的外围上下文是包含它的 outer
函数的执行上下文因此,inner
函数可以访问 outer
函数中的变量 outerVar
另一个例子是使用事件处理函数时:
<!DOCTYPE html>
<html>
<head>
<title>Outer Context</title>
</head>
<body>
<button id="myButton">Click Me</button>
<script>
const button = document.getElementById('myButton');
button.addEventListener('click', function() {
console.log(this); // 这里的 this 是事件目标,即按钮元素
console.log('Button clicked!');
});
</script>
</body>
</html>
当按钮被点击时,事件处理函数中的 this
指向事件目标,即按钮元素这是因为事件处理函数的外围上下文是事件目标的上下文
全局作用域中的外围上下文: 在全局作用域中,外围上下文就是全局上下文~
console.log(this === window); // 在浏览器环境中输出 true,因为 this 在全局作用域中指向全局对象 window
外围上下文就是包裹当前代码块的上一层环境,它决定了代码中的 this
、变量访问等行为
不同的this
- 这里再精简一下this,既然说了,一下子多拓展一点
当在不同的上下文中输出 this
时,会得到不同的结果以下是几个例子来演示不同上下文中的 this
:
全局上下文中的 this
:
console.log(this); // 在浏览器环境中输出全局对象 window,在 Node.js 中输出全局对象 global
对象方法中的 this
:
const person = {
name: 'John',
sayHello: function() {
console.log(this); // 指向当前对象 person
}
};
person.sayHello();
构造函数中的 this
:
function Person(name) {
this.name = name;
console.log(this); // 指向新创建的对象实例
}
const john = new Person('John');
箭头函数中的 this
:
const myArrowFunction = () => {
console.log(this); // 在浏览器环境中指向全局对象 window(如果没有外围上下文的话)
};
myArrowFunction();
加下外围函数的this:
当在外围函数中使用箭头函数时,箭头函数会继承外围函数的上下文这意味着,在外围函数中定义的箭头函数会共享外围函数的 this
值让我们来看一个示例:
function outerFunction() {
console.log(this); // 外围函数的 this
const innerArrow = () => {
console.log(this); // 继承外围函数的 this
};
innerArrow();
}
const myObject = {
name: 'John',
outer: outerFunction
};
myObject.outer(); // 外围函数的 this 是 myObject,箭头函数继承了外围函数的 this
再来一个,加强理解
在data中的this:
data: {
name: '即兴小索奇',
age: '6',
showInfo(){
console.log(this) // 输出window
}}
在全局作用域(即没有嵌套在任何函数或对象中)中定义对象字面量,这个函数实际上会成为全局对象的属性,那么对象字面量内部的函数的 this
默认会指向全局对象 window
所以,在对象字面量中定义了 showInfo
方法时,它的上下文(this
的值)会指向全局对象 window
,而不是 Vue 实例
事件修饰符
用不同的事件修饰符时,以不同的方式行动
重点
- **
.prevent
**:这个修饰符就像是阻止事件按照平常的方式去做举个例子,你点击一个链接,但是加了.prevent
修饰符,链接就不会打开新页面 - **
.stop
**:阻止事件传播给其他元素(阻止事件冒泡) - **
.once
**:只会触发一次事件,后面再次点击事件就无效了 - **
.capture
**:让事件监听器在捕获阶段就能听到事件,而不是等到冒泡阶段就好像是你提前听到了一个消息,比其他人都早 - **
.self
**:只有event.target是当前操作的元素时事件才会被触发; - **
.passive
**:事件的默认行为立即执行,告诉事件先做你的事情,无需等待事件回调执行完毕
修饰符可以连着写,比如@click.stop.prevent
索奇问答
A:什么是事件冒泡?
Q:事件冒泡是指当在DOM中触发一个事件时,事件会从触发的元素开始,然后逐级向上(向外)传播到DOM树的更高层次的元素,直到达到根节点为止这个过程就像气泡一样从水底冒到水面,所以称为“冒泡”
看下面的代码
<div class="demo1" @click="showInfo">
<button @click="showInfo">看我小索奇冒泡</button>
</div>
new Vue({
el: '.demo1',
methods: {
showInfo: function() {
console.log('看我小索奇冒泡');
}
}
});
当你点击按钮时,以下事情会发生:
- 首先,按钮的点击事件会触发,因为按钮也绑定了
@click
事件这时会执行按钮自己的showInfo
方法,控制台会打印出 "看我小索奇冒泡" - 事件会冒泡到外部的
div
元素,也会执行div
绑定的showInfo
方法,再次打印出 "看我小索奇冒泡"
弹窗两次
事件冒泡
点击按钮会依次触发按钮自己的点击事件处理程序和外部 div
元素的点击事件处理程序,这就是事件冒泡的过程
如果你想要阻止事件冒泡,就需要用到上面提到的修饰符,如 @click.stop
修饰符 stop
会阻止事件继续向上层元素传播,这样只会触发按钮自己的点击事件处理程序,不会再触发外部 div
元素的点击事件处理程序
什么是capture
?
简记重点:捕获是从外到里,冒泡是从里到外
当加上capture
时,即使点击box2
,box1
在捕获阶段就开始处理了,即使点击2
事件,先触发的也是1
事件
<div class="box1" @click.capture="showMsg(1)">
div1
<div class="box2" @click="showMsg(2)">
div2
</div>
</div>
什么是self
?
// 上面的@click.self只是管div的,只有target是div时候才会调用它的方法
<div class="container" @click.self="handleContainerClick">
<button @click="handleButtonClick">点我提示信息</button>
</div>
methods: {
handleContainerClick(event) {
console.log("Container被点击,触发了handleContainerClick方法");
console.log("事件目标 (target):", event.target);
},
handleButtonClick() {
console.log("按钮被点击,触发了handleButtonClick方法");
}
}
- 由于我们在外部的
container
元素上使用了.self
修饰符,只有点击container
元素自身时,才会触发handleContainerClick
方法如果点击按钮,事件目标是按钮,所以不会触发container
上的点击事件处理程序 - 无论点击
container
元素的哪个部分,都只有在点击container
自身时才会触发点击事件处理程序点击按钮,事件会从按钮冒泡到container
,但由于我们使用了.self
修饰符,它不会触发container
上的点击事件处理程序
可能有点绕吧...仔细理解一下~
详解passive
- 在给移动端使用的时候用的会偏多一些
passive
是一个用于优化浏览器滚动性能的事件修饰符在Web开发中,滚动事件(例如滚动页面或元素)可能会触发一些复杂的处理,这可能导致页面性能下降,因为浏览器需要等待事件处理程序完成才能继续执行默认滚动行为
通过使用 passive
修饰符,可以告诉浏览器事件监听器,使浏览器在执行事件处理程序时更加高效,不必等待事件处理完成这对于优化滚动性能非常有帮助~
看下面代码演示
<style>
#scroll-container {
height: 400px;
overflow: auto;
}
ul li {
border-left: 50px;
height: 400px;
width: 400px;
background-color: aquamarine;
}
</style>
</head>
<body>
<div id="app">
<!-- 使用 passive 优化滚动性能 -->
<div id="scroll-container" @scroll.passive="handleScroll('优化')">
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
</ul>
</div>
</div>
<script type="text/JS">
new Vue({
el: '#app',
methods: {
handleScroll(type) {
console.log(`${type}滚动事件处理`);
}
}
});
</script>
</body>
滚动时,不用等待事件处理完成才能够执行操作
键盘事件
键盘上的每个按键都有自己的名称和编码,例如:回车键是13
Vue还对一些常用按键起了别名方便使用
Vue中常用的按键别名有下面一些(注意:别名是小写的,大些也可以,但是为了方便大多数写小写)
记住一下常见的即可
- 回车
enter
- 删除
delete
(退格和删除键都能够捕获) - 退出
esc
- 空格
space
- 换行
tab
特殊 - 上下左右分别对应
up
、down
、left
、right
Q:不用别名如何使用?
A:需要配合键值达到效果
<body>
<div id="root">
<h2>{{name}}</h2>
<input type="text" placeholder="按下回车提示输入" @keyup = "showInfo">
</div>
<script type="text/JS">
Vue.config.productionTip = false
new Vue({
el:'#root',
data:{
name:"即兴小索奇"
},
methods:{
showInfo(e){
// console.log(e.target.value) //输入什么出什么value
if(e.keyCode !=13){
console.log(e.target.value) // 当点击回车的时候再显示对应的value
}
console.log(e.keyCode)
}
}
})
</script>
</body>
上面配合别名只需要在@keyup
后面添加别名即可,比如上面的案例使用 @keyup.enter
即可,省去了非常多的麻烦
拓展
Vue未提供别名的按键,可以使用按键原始的key值去绑定,但注意要转为kebab-case(烤肉串格式)
如:给CapsLock绑定事件 = @keyup.caps-lock
tips:可以借助下面获取键名和键码
想要获取每一个键名和键码可以用event.key
,event.keyCode
console.log(e.key,e.keyCode)
image-20230818010846033
其中需要注意一些按键
- tap:因为它的作用就是跳出,所以需要配合
keyup.down
按下时候就触发的事件使用,否则没效果
特殊:系统修饰键(用法特殊)ctrl、alt、shift、meta(meta就是win菜单键)
都一样的道理,比如@keydown.ctrl
- 配合keyup使用:按下修饰键的同时,再按下其他键,随后释放其他键,事件才被触发
- 配合keydown:能够正常触发事件,不用搭配其他按键
自定义别名
// 定义一个name 它的keyCode是66
vue.config.keyCodes.name = 66
对您有用的话点个免费的赞叭~