【Vue】上万个字把事件处理讲解的淋漓尽致

2023-09-21 10:43:13

hello,我是小索奇,精心制作的Vue系列教程持续更新哈,想要学习&巩固&避坑就一起学习吧~

事件处理

事件的基本用法

重点内容

  1. 使用v-on:xxx缩写@xxx绑定事件,其中 xxx 是事件名(回顾:v-bind缩写为冒号:)

  2. 事件的回调需要配置在methods对象中,最终会在VM

  3. methods中配置的函数,不要用箭头函数,否则 this 就不是VM了,重点强调!

  4. methods中配置的函数,都是被 Vue所管理的函数,this 的指向是VM(ViewModel-vue实例对象)或组件实例对象

  5. @click="demo"@click="demo($event)"效果一致,前者是默认会传event,省略了而已,后者更加灵活,可以传更多参数

  6. 当我们需要给函数传参时@click='showInfo(666)'怎么获取event

  7. 关于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

image-20230812205755903

这里定义了一个普通的JS 函数 showInfo(),但没有将它添加到 Vue 实例的 methods 选项中,因此在模板中使用 v-on:click="showInfo" 时无法正确调用该函数我们需要将 showInfo 函数添加到 Vue 实例的 methods 选项中

因为 Vue 实例的 methods 选项专门用于存放可以在模板中调用的方法

当在methods中加上showInfo方法就会成功显示

image-20230812211754411

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

image-20230812213221250

在这里这个事件就是button

event.target就是这个事件目标(button)

image-20230812213746135

image-20230812213746135

console.log(event)输出可以看到所有属性及方法

image-20230812213517353

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 取决于外围上下文

这种行为使得箭头函数在某些情况下非常有用,例如在回调函数中,你可以捕获外部函数的上下文,避免了在回调中使用 thatself 来保存上下文

总结来说,普通函数中的 this 在调用时会根据调用方式和上下文而变化,而箭头函数中的 this 始终继承自外围上下文,不会随着调用方式的改变而改变

拓展一下

在箭头函数中,this 的取值是由箭头函数所在代码块的上下文决定的换句话说,箭头函数的 this 继承自最近的非箭头函数父级的 this

这意味着,如果箭头函数直接位于全局作用域中,那么它的 this 就会继承自全局对象(例如,在浏览器环境中,就是 window 对象)

让我们来看一个示例:

const arrowFunction = () => {
    console.log(this);
};
arrowFunction(); // 输出的 this 取决于全局上下文,通常是 window 或 global 对象(取决于环境)

在浏览器环境中运行上述代码,arrowFunctionthis 将指向全局对象 window

然而,如果箭头函数嵌套在其他函数或对象方法中,它的 this 将继承自外围上下文:

function outerFunction() {
    const innerArrow = () => {
        console.log(this);
    };
    innerArrow();
}

outerFunction(); // 输出的 this 取决于 outerFunction 的上下文

在这个示例中,innerArrowthis 将继承自 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 实例

事件修饰符

用不同的事件修饰符时,以不同的方式行动

重点

  1. **.prevent**:这个修饰符就像是阻止事件按照平常的方式去做举个例子,你点击一个链接,但是加了.prevent修饰符,链接就不会打开新页面

  2. **.stop**:阻止事件传播给其他元素(阻止事件冒泡)

  3. **.once**:只会触发一次事件,后面再次点击事件就无效了

  4. **.capture**:让事件监听器在捕获阶段就能听到事件,而不是等到冒泡阶段就好像是你提前听到了一个消息,比其他人都早

  5. **.self**:只有event.target是当前操作的元素时事件才会被触发;

  6. **.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('看我小索奇冒泡');
    }
  }
});

当你点击按钮时,以下事情会发生:

  1. 首先,按钮的点击事件会触发,因为按钮也绑定了 @click 事件这时会执行按钮自己的 showInfo 方法,控制台会打印出 "看我小索奇冒泡"

  2. 事件会冒泡到外部的 div 元素,也会执行 div 绑定的 showInfo 方法,再次打印出 "看我小索奇冒泡"

弹窗两次

事件冒泡

事件冒泡

点击按钮会依次触发按钮自己的点击事件处理程序和外部 div 元素的点击事件处理程序,这就是事件冒泡的过程

如果你想要阻止事件冒泡,就需要用到上面提到的修饰符,如 @click.stop修饰符 stop 会阻止事件继续向上层元素传播,这样只会触发按钮自己的点击事件处理程序,不会再触发外部 div 元素的点击事件处理程序

什么是capture

简记重点:捕获是从外到里,冒泡是从里到外

当加上capture时,即使点击box2box1在捕获阶段就开始处理了,即使点击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特殊

  • 上下左右分别对应updownleftright

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.keyevent.keyCode

console.log(e.key,e.keyCode)

image-20230818010846033

image-20230818010846033

其中需要注意一些按键

  • tap:因为它的作用就是跳出,所以需要配合keyup.down按下时候就触发的事件使用,否则没效果

特殊:系统修饰键(用法特殊)ctrl、alt、shift、meta(meta就是win菜单键)

都一样的道理,比如@keydown.ctrl

  • 配合keyup使用:按下修饰键的同时,再按下其他键,随后释放其他键,事件才被触发

  • 配合keydown:能够正常触发事件,不用搭配其他按键

自定义别名

// 定义一个name 它的keyCode是66
vue.config.keyCodes.name = 66 

对您有用的话点个免费的赞叭~

更多推荐

Unity中Shader特性PerRendererData

文章目录前言一、优化前是对使用了相同材质球的不同物体间shader分别设置,比较消耗性能二、使用[PerRendererData]标签,可以在脚本中使用SetPropertyBlock()对使用同一材质球的不同物体进行修改其Shader属性前言Unity中Shader特性PerRendererData一、优化前是对使用

科技资讯|Canalys发布全球可穿戴腕带设备报告,智能可穿戴增长将持续

市场调查机构Canalys近日发布报告,表示2023年第2季度全球可穿戴腕带设备出货量达4400万台,同比增长了6%。主要归功于其亲民的价格以及消费者对价位较高的替代品仍持谨慎态度,基础手环市场尽管与去年同期相比有所下降,仍然保持稳定的市场份额,约为19%。可穿戴设备仍然具有长期的发展前景。尽管短期经济因素使消费者更倾

聊聊Spring中循环依赖与三级缓存

先看几个问题什么事循环依赖?什么情况下循环依赖可以被处理?spring是如何解决循环依赖的?什么是循环依赖?简单理解就是实例A依赖实例B的同时B也依赖了A@ComponentpublicclassA{//A中依赖B@AutowiredprivateBb;}@ComponentpublicclassB{//B中依赖A@A

spring boot 八、 sharding-jdbc 分库分表 按月分表

在项目resources目录下新建com.jianmu.config.sharding.DateShardingAlgorithm文件新增yaml配置数据源spring:shardingsphere:props:sql:#是否在日志中打印SQLshow:true#打印简单风格的SQLsimple:truedatasou

JavaWeb概念视频笔记

学习地址:102.尚硅谷_Tomcat-Tomcat服务器和Servlet版本的对应关系_哔哩哔哩_bilibili目录1.JavaWeb的概念2.Web资源的分类3.常用的Web服务器4.Tomcat服务器和Servlet版本的对应关系5.Tomcat的使用a.安装b.目录介绍c.如何启动Tomcat服务器另一种启动

9.19号作业

2>完成文本编辑器的保存工作widget.h#ifndefWIDGET_H#defineWIDGET_H#include<QWidget>#include<QFontDialog>#include<QFont>#include<QMessageBox>#include<QDebug>#include<QColorDia

[npm]脚手架本地全局安装1

[npm]脚手架本地全局安装1npmlink全局安装npminstall全局安装卸载全局安装的脚手架该文章是你的脚手架已经开发完成的前提下,你想要本地全局安装该脚手架,便于本地使用脚手架的命令的情况npmlink全局安装如果本地开发的项目是个脚手架,只是个人使用,也并不需要上传到npm或者私库,如何安装本地的项目到包的

设计模式(2) - 创建型模式

创建型模式指的是创建对象或是获取实例的方式。1、工厂模式平时写一些简单的代码可能会直接用new创建出一个对象,但是实际在阅读一些功能比较多、规模比较庞大的工程时,可能会发现有多个类继承于同一个基类的情况,它们拥有同样的接口但是实现了不同的功能。它们可能是可以互相替代的两套系统(例如AndroidMedia中的ACode

Docker 容器设置为自动重启

Docker自动重启原因Docker自动重启通常是由以下几个原因导致的:程序崩溃系统内存不足系统进程使用过多CPU和RAM导致的阻塞docker容器被杀死或重新启动,导致应用程序中断网络中断当这些问题出现时,Docker会自动重启运行中的服务来尝试解决问题。dockerupdate--restart=alwaysmys

【docker】容器跟宿主机、其他容器通信

说明容器跟宿主机、其他容器通信的关键在于它们要在同一个网络,或者通过修改路由信息来可以让它们互相之间能够找得到对方的IP。本文主要介绍让它们在同一个网络的方法。Docker自定义网络模式介绍Docker容器可以通过自定义网络来与宿主机或其他容器进行通信。在Docker中,有三种类型的网络:bridge网络、host网络

使用ElementPlus实现内嵌表格和内嵌分页

前言有时遇到这样的需求,就是在表格里面嵌入一个表格,以及要求带有分页,这样在ElementPlus中很好实现。以下使用Vue2语法实现一个简单例子,毕竟Vue3兼容Vue2语法,若想要Vue3版本例子,简单改改就OK了。一、示例代码(1)/src/views/Example/InlineTable/index.vue<

热文推荐