Vue(二):事件处理,计算属性、监视属性及二者的区别

news/2024/7/10 1:48:08 标签: vue.js, 前端, javascript, vue

一、事件相关内容

1、事件处理


1、使用v-on:xxx或@xxx绑定事件,其中xxx是事件名。
2、事件的回调需要配置在methods对象中,最终会在vm上。
3、methods中配置的函数,不要用箭头函数!否则this就不是vm了。
4、methods中配置的函数,都是被Vue所管理的函数,this的指向是vm 或 组件实例对象。
5、@click="demo" 和 @click="demo($event)" 效果一致,但后者可以传参。
6、如果不传参,默认第一个形参是事件对象;如果传参,按照参数顺序来接,如果想接事件对象,要传入$event关键字

javascript"><body>
    <!-- 准备好一个容器 -->
    <div id="root">
        <h2>欢迎来到{{name}}学习</h2>
        <!-- <button v-on:click="showInfo">点我提示信息</button> -->
        <button @click="showInfo1">点我提示信息1(不传参)</button>
        <button @click="showInfo2(66,$event)">点我提示信息2(传参)</button>
    </div>
    <script>
        Vue.config.productionTip = false //阻止Vue在启动时生成提示

        const vm = new Vue({
            el:'#root',
            data:{
                name:'椰果'
            },
            methods:{
                showInfo1(event){
                    //当使用箭头函数时,this是在编译前绑定的,指向上一级window
                    //console.log(this);//此处的this是指vm
                    alert('同学你好!')
                },
                showInfo2(number,a){
                    console.log(number,a);
                    //当使用箭头函数时,this是在编译前绑定的,指向上一级window
                    // console.log(event.target.innerText);
                    //console.log(this);//此处的this是指vm
                    alert('同学你好!!')
                }
            }
        })
    </script>
</body>

2、事件修饰符

Vue.js 通过由点 . 表示的指令后缀来调用修饰符。

  • .stop - 阻止冒泡
  • .prevent - 阻止默认事件
  • .capture - 阻止捕获
  • .self - 只监听触发该元素的事件
  • .once - 只触发一次

 

javascript"><!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>事件修饰符</title>
    <script src="../js/vue.js>vue.js"></script>
    <style>
        *{
            margin-top: 20px;
        }
        .box1{
            padding: 5px;
            background-color: green;
        }
        .box2{
            padding: 5px;
            background-color: red;
        }
        .list{
            height: 200px;
            width: 200px;
            background-color: aqua;
            overflow: auto;
        }
        li{
            height: 100px;
        }
    </style>
</head>
<body>
    <!-- 
        1.prevent:阻止默认事件(常用);
        2.stop:阻止事件冒泡(常用);
        3.once:事件只触发一次(常用);
        4.capture:使用事件的捕获模式;(捕获由外向里,冒泡由里向外)
        5.self:只有event.target是当前操作的元素是才触发事件;
        6.passive:事件的默认行为立即执行,无需等待事件回调执行完毕;
     -->
    <!-- 准备好一个容器 -->
    <div id="root">
        <h2>欢迎来到{{name}}学习</h2>
        <!-- 1.prevent: 阻止默认事件(常用) -->
        <a href="http://www.atguigu.com" @click.prevent="showInfo">点我提示信息</a>
        <!-- 2.stop:阻止事件冒泡(常用); -->
        <div class="demo1" @click="showInfo">
            <button @click.stop="showInfo">点我提示信息</button>
        </div>
        <!-- 3.once:事件只触发一次(常用); -->
        <button @click.once="showInfo">点我提示信息</button>
        <!-- 4.capture:使用事件的捕获模式 -->
        <div class="box1" @click.capture="showMsg(1)">
            box1
            <div class="box2" @click="showMsg(2)">
                box2
            </div>
        </div>
        <!-- 5.self:只有event.target是当前操作的元素是才触发事件,也可以阻止冒泡? -->
        <div class="demo1" @click.self="showInfo">
            <button @click="showInfo">点我提示信息</button>
        </div>
        <!-- 6.passive:事件的默认行为立即执行,无需等待事件回调执行完毕 -->
        <!-- <ul @scroll="demo" class="list"> 滚动条 -->
        <ul @wheel.passive="demo" class="list"> 
            <!-- 鼠标的滚轮 -->
            <li>1</li>
            <li>2</li>
            <li>3</li>
            <li>4</li>
        </ul>
    </div>
    <script>
        new Vue({
            el:"#root",
            data:{
                name:'椰果'
            },
            methods:{
                showInfo(e){
                    alert('同学你好')
                },
                showMsg(msg) {
                    console.log(msg);
                },
                demo() {
                    for (let i = 0; i < 10000; i++) {
                        console.log('#');
                    }
                    console.log('累坏了');
                }
            }
        })
    </script>
</body>
</html>

3、键盘事件

 

javascript"><!-- 准备好一个容器 -->
<div id="root">
    <h2>我的名字叫{{name}}</h2>
    <input type="text" placeholder="按下回车输出" @keyup.enter="showInfo">
    <input type="text" placeholder="按下回车输出" @keyup.13="showInfo">
    <input type="text" placeholder="按下回车输出" @keyup.huiche="showInfo">
    <input type="text" placeholder="按下删除或退格输出" @keyup.delete="showInfo">
    <input type="text" placeholder="按下切换大小写输出" @keyup.caps-lock="showInfo">
</div>

<script>
    Vue.config.keyCodes.huiche = 13; //定义一个别名为huiche的enter按键

    const vm = new Vue({
        el: '#root',
        data: {
            name: 'zzy',
            age: 18
        },
        methods: {
            showInfo(e) {
                // if (e.keyCode !== 13) return  //原生js实现按下回车输出值
                console.log(e.target.value);
            }
        }
    })
</script>

1、Vue未提供别名的按键,可以使用按键原始的key值去绑定,但注意要转为kebab-case(短横线命名)
比如我想按下切换大小写的按键就触发showInfo:

<input type="text" placeholder="按下切换大小写输出" @keyup.caps-lock="showInfo">

2、系统修饰键(用法特殊):ctrl、alt、shift、meta

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

3、也可以使用keyCode去指定具体的按键(不推荐)

<input type="text" placeholder="按下回车输出" @keyup.13="showInfo">

4、Vue.config.keyCodes.自定义键名 = 键码,可以去定制按键别名

Vue.config.keyCodes.huiche = 13; //定义一个别名为huiche的enter按键

 4.补充两个细节

1、修饰符可以连续写

<button @click.stop.prevent="showInfo">你好</button>

2、如果先让两个键同时按下才触发,键名也可以连续绑定
比如按下ctrl+y触发showInfo方法

<input type="text" @keyup.ctrl.y="showInfo">

 二、计算属性


顾名思义,计算属性就是计算出来的属性,英文名儿computed
这里要和data和methods里的东西区分,data里的属性,methods里的函数,你写的都会原封不动放到vm里,但是computed里面的东西写的是对象,最后放到vm里的是这个对象的名,值是get里的返回值。

1. 使用插值语法和methods拼接姓名

使用插值语法,如果简单点还行,如果复杂了,里面的逻辑写一大堆,很难看
使用methods不错,但是有一个问题,就是每次用到fullName都会调用一次,缓存太多

javascript"><!-- 准备好一个容器 -->
<div id="root">
    姓: <input type="text" v-model="firstName"><br><br>
    名: <input type="text" v-model="lastName"><br><br>
    <!-- 拼接姓名实现"张-三"联动 -->
    <!-- 第一种实现:使用插值语法(字符串的slice方法,左闭右开截取字符串) ,麻烦-->
    姓名: <span>{{firstName.slice(0,3)}}-{{lastName}}</span><br><br>


    <!-- 第二种实现:使用methods,每次用到都会调用一次,而计算属性只调用第一次就欧了-->
    姓名: <span>{{fullName()}}</span><br><br>
    姓名: <span>{{fullName()}}</span><br><br>
    姓名: <span>{{fullName()}}</span><br><br>
    姓名: <span>{{fullName()}}</span><br><br>
    姓名: <span>{{fullName()}}</span>
</div>

<script>
    const vm = new Vue({
        el: '#root',
        data: {
            firstName: '张',
            lastName: '三'
        },
        methods: {
            fullName() {
                console.log('调用')
                return this.firstName + '-' + this.lastName;
            }
        }
    })
</script>

2. 使用计算属性拼接姓名

1、没有缓存问题,第一次调用之后就不会再调用,节省内存
2、计算属性里面的属性要写成对象的形式,每个对象里面都有getter和setter
3、fullName实际上就是firstName和lastName经过一番计算得到的玩意儿,直接到vm身上成为vm的属性
4、当有人读取fullName时,get调用,返回值作为fullName的值
5、set什么时候调用? fullName被修改时调用,这里边呢有个连锁反应,我手动修改vm.fullName导致firstName和lastName被修改,Vue模板重新解析,页面刷新,firstName和lastName的修改又导致get被重新执行(依赖的数据变了),返回新的fullName,计算属性计算属性,就是一直在计算,所以要想改fullName,必须改它依赖的值才行。

    <!-- 准备好一个容器 -->
    <div id="root">
        姓:<input type="text" v-model="firstnmae"> <br><br>
        名:<input type="text" v-model="lastnmae"> <br><br>
        全名:<span>{{fullname}}</span>
    </div>
    <script>
        const vm = new Vue({
            el: "#root",
            data: {
                firstname:'你',
                lastname:'鹏'
            },
            computed:{
                fullname:{
                    // get有什么作用?当有人读取ful1Name时,get就会被调用,且返回值就作为fullName的值
                    // get什么时候调用?1.初次读取fullName时。2.所依赖的数据发生变化时。
                    get(){
                        console.log('被调用了');
                        return this.firstname + '-' + this.lastname;
                    },
                //set什么时候调用? fullName被手动修改时调用
                //这里边呢有个连锁反应,我手动修改vm.fullName导致firstName和lastName被修改,Vue模板重新解析
                //页面刷新,firstName和lastName的修改又导致get被重新执行(依赖的数据变了),返回fullName
                //计算属性计算属性,就是一直在计算,所以要想改fullName,必须改它依赖的值
                set(val){
                    let arr = val.split('-');
                    this.firstName = arr[0];
                    this.lastName = arr[1];
                }
                } 
            }
        })
    </script>
</body>

 3、计算属性简写

只考虑读取不考虑修改时可使用简写:

javascript">    <!-- 准备好一个容器 -->
    <div id="root">
        姓: <input type="text" v-model="firstName"><br><br>
        名: <input type="text" v-model="lastName"><br><br>
        <!-- 拼接姓名实现"张-三"联动 -->
    
        <!-- 使用计算属性,只调用一次,非常奈斯,节省内存 -->
        姓名: <span>{{fullName}}</span>
    </div>
    
    <script>
        const vm = new Vue({
            el: '#root',
            data: {
                firstName: '张',
                lastName: '三'
            },
            computed: {
                fullName() {
                    console.log('fullName被读了');
                    return this.firstName + '-' + this.lastName;
                }

            }
        })
    </script>

 这里一定要注意,这个fullName是一个属性,不是一个方法,只不过是调用里面的getter,把返回值直接给了vm罢了,所以直接{{fullName}}就可以搞出来,不用加小括号调用!

 4、计算属性总结

3. 计算属性总结
定义:要用的属性不存在,要通过Vue中已有属性计算得来。
原理:底层借助了Objcet.defineproperty方法提供的getter和setter。
get函数什么时候执行?
(1).初次读取时会执行一次。
(2).当依赖的数据发生改变时会被再次调用。
优势:与methods实现相比,内部有缓存机制(复用),效率更高,调试方便。
备注:
1.计算属性最终会出现在vm上,直接读取使用即可,不用加括号,和methods区别一下子。
2.如果计算属性要被修改,那必须写set函数去响应修改,且set中要引起计算时依赖的数据发生改变,这很关键。

三、监视属性

1、天气案例

首先用计算属性写一下天气切换案例

javascript">    <!-- 准备好一个容器 -->
    <div id="root">
        <h2>今天天气很{{info}},{{x}}</h2>
        <button @click="changeweather">切换天气</button>
    </div>
    <script>
        new Vue({
            el:'#root',
            data:{
                ishot:true,
                x:1
            },
            computed:{
                info(){
                    return this.ishot ? '炎热' : '寒冷'
                }
            },
            methods: {
                changeweather(){
                    this.ishot = !this.ishot
                    this.x++
                }
            }
        })
    </script>

 

2、监视属性

(1).new Vue时传入watch配置

javascript"><div id="root">
    <!-- 实现点击按钮切换天气 -->
    <!-- 写法1:利用插值语法和三元表达式 -->
    <h2>今天天气很{{isHot ? '炎热': '寒冷'}}</h2>

    <!-- 写法2:利用计算属性 -->
    <h2>今天天气很{{info}}</h2>
    <!--    绑定事件的时候:@xxx="yyy" yyy可以写一些简单的语句,但是最好别这么干-->
    <!--    <button @click="isHot = !isHot">切换天气</button>-->
    <button @click='change'>点击切换天气</button>
</div>
<script>
    new Vue({
        el: '#root',
        data: {
            isHot: true
        },
        methods: {
            change() {
                this.isHot = !this.isHot;
            }
        },
        computed: {
            info() {
                //注意这里的isHot要加this
                return this.isHot ? '炎热' : '寒冷';
            }
        },
        watch: {
            isHot: {
                immediate: true,   //初始化时先调用一次handler 
                //当isHot被修改时调用handler函数
                handler(newVal, oldVal) {
                    console.log('isHot被修改了', '改之前是' + oldVal, '改之后是' + newVal);
                }
            },
            //watch不只可以监视data中的属性,还可以监视计算属性
            info: {
                immediate: true,   //初始化时先调用一次handler 
                //当isHot被修改时调用handler函数
                handler(newVal, oldVal) {
                    console.log('info被修改了', '改之前是' + oldVal, '改之后是' + newVal);
                }
            }
        }
    })  
</script>

 (2)通过vm.$watch监视

javascript">const vm = new Vue({
    el: '#root',
    data: {
        isHot: true
    },
    methods: {
        change() {
            this.isHot = !this.isHot;
        }
    },
    computed: {
        info() {
            //注意这里的isHot要加this
            return this.isHot ? '炎热' : '寒冷';
        }
    }
})

	vm.$watch('isHot', {
	    immediate: true,   //初始化时先调用一次handler 
	    //当isHot被修改时调用handler函数
	    handler(newVal, oldVal) {
	        console.log('isHot被修改了', '改之前是' + oldVal, '改之后是' + newVal);
	    }
	})
	
	vm.$watch('info', {
	    immediate: true,   //初始化时先调用一次handler 
	    //当isHot被修改时调用handler函数
	    handler(newVal, oldVal) {
	        console.log('info被修改了', '改之前是' + oldVal, '改之后是' + newVal);
	    }
	})

3、深度监视 

(1).Vue中的watch默认不监测对象内部值的改变(一层,如果监视那么对象地址不变就不变)。
(2).配置deep:true可以监测对象内部值改变(多层)。
备注:
(1).Vue自身可以监测对象内部值的改变,但Vue提供的watch默认不可以!想要可以就要手动开启deep:true
(2).使用watch时根据数据的具体结构,决定是否采用深度监视

javascript">   <!-- 准备一个容器 -->
    <div id="root">
        <h3>a的值是:{{numbers.a}}</h3>
        <button @click="numbers.a++">点我实现a+1</button>
        <h3>b的值是:{{numbers.b}}</h3>
        <button @click="numbers.b++">点我实现b+1</button>
        <h3>我tm就非要强制让number变</h3>
        <button @click="numbers = {a:666, b:999}">点我改变numbers</button>
    </div>
    <script>
        const vm = new Vue({
            el: '#root',
            data: {
                isHot: true,
                numbers: {
                    a: 1,
                    b: 2
                }
            },
            watch: {
                //监视多级结构中某个属性的变化,使用引号包起来(之前不加引号都是简写形式)
                'numbers.a': {
                    handler() {
                        console.log('a被改变了');
                    }
                },
                //如果下面这么写,即使ab变了也不会执行handler,因为这么写意思是监视numbers这个属性
                //而这个属性值是一个对象,只要numbers对象的地址不变就不变,除非像上面div写的暴力方法
                 numbers: {
                     handler() {
                         console.log('numbers改变了');
                     }
                 },

                //但是如果加个deep,就可以监视多级结构中某个属性的变化,ab变了numbers也变
                numbers: {
                    deep: true,
                    handler() {
                        console.log('numbers改变了');
                    }
                },
            }
        })
    </script>

4、监视属性简写 

 形式1:

javascript">	watch: {
                //复杂写法:
                 isHot: {
                    //immediate: true,   //初始化时先调用一次handler 
                    //当isHot被修改时调用handler函数
                    handler(newVal, oldVal) {
                        console.log('isHot被修改了', '改之前是' + oldVal, '改之后是' + newVal);
                     }
                },
                
				//简写
                // 如果handler函数中没有其他的配置项(deep,immediate等),可以简写成以下形式
                isHot(newVal, oldVal) {
                    console.log('isHot被修改了', '改之前是' + oldVal, '改之后是' + newVal);
                }
            }

写法2:

javascript">	//复杂形式
	vm.$watch('isHot', {
	    //当isHot被修改时调用handler函数
	    handler(newVal, oldVal) {
	        console.log('isHot被修改了', '改之前是' + oldVal, '改之后是' + newVal);
	    }
	})
	//简写形式
	vm.$watch('isHot',function (newVaule,oldVaule) {
	        console.log(newVaule,oldVaule);
	    })

 四、computed对比watch

1.对比拼接姓名案例:

<!-- 准备好一个容器 -->
<div id="root">
    姓: <input type="text" v-model="firstName"><br><br>
    名: <input type="text" v-model="lastName"><br><br>
    <!-- 拼接姓名实现"张-三"联动 -->
    姓名: <span>{{fullName}}</span>
</div>

计算属性: 

javascript">const vm = new Vue({
    el: '#root',
    data: {
        firstName: '张',
        lastName: '三'
    },
    computed: {
        fullName() {
            console.log('fullName被读了');
            return this.firstName + '-' + this.lastName;
        }

    }
})

监视属性: 

javascript">   const vm = new Vue({
        el: '#root',
        data: {
            firstName: '张',
            lastName: '三',
            fullName: '张-三'
        },
        watch: {
            firstName: {
                handler(newVal, oldVal) {
                    this.fullName = newVal + '-' + this.lastName;
                }
            },
            lastName: {
                handler(newVal, oldVal) {
                    this.fullName = this.firstName + '-' + newVal;
                }
            },
        }
    })

这么看起来好像计算属性更简单,那么计算属性就真的牛逼吗?我觉得watch更牛逼,因为它可以异步。
比如我如果手动改了firstName要等一秒钟再改fullName呢?
用监视属性,我们就可以这么写:

javascript">watch: {
    firstName: {
        handler(newVal, oldVal) {
            setTimeout(() => {
                this.fullName = newVal + '-' + this.lastName;
            }, 1000);
        }
    }
}

但是computed就没辙了,因为等一秒再return?

2. 总结:


1、computed能完成的功能,watch都可以完成。
2、watch能完成的功能,computed不一定能完成,例如:watch可以进行异步操作。

两个重要的小原则!
1、所被Vue管理的函数,最好写成普通函数,这样this的指向才是vm 或 组件实例对象。
2、所有不被Vue所管理的函数(定时器的回调函数、ajax的回调函数等、Promise的回调函数),最好写成箭头函数,这样this的指向才是vm 或 组件实例对象。

 (ps:这一部分老师讲的很细,但是又有点绕,后面还需要再多加练习)

 

 


http://www.niftyadmin.cn/n/5419173.html

相关文章

高级语言讲义2008软专(仅高级语言部分)

1.我们可以用两个整型变量表示分数&#xff0c;其中一个表示分子&#xff0c;另一个表示 分母&#xff0c;例如: m3, n5,表示3/5 .编一程序&#xff0c;输入两个变量m (表示分子)&#xff0c; n (表示分母), 求其最简分数。例如:输入m3, n9,输出1/3 #include <stdio.h>…

按图搜索淘宝商品(拍立淘)

使用淘宝拍立淘接口的步骤通常包括&#xff1a; 注册成为淘宝开放平台的开发者&#xff1a;在淘宝开放平台网站上注册账号并完成认证。 创建应用以获取API密钥&#xff1a;在您的开发者控制台中创建一个应用&#xff0c;并获取用于API调用的密钥&#xff0c;如Client ID和Clie…

2024年【化工自动化控制仪表】新版试题及化工自动化控制仪表考试试题

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 化工自动化控制仪表新版试题是安全生产模拟考试一点通总题库中生成的一套化工自动化控制仪表考试试题&#xff0c;安全生产模拟考试一点通上化工自动化控制仪表作业手机同步练习。2024年【化工自动化控制仪表】新版试…

蓝桥杯2023年-岛屿个数(dfs,染色法)

题目描述 小蓝得到了一副大小为 M N 的格子地图&#xff0c;可以将其视作一个只包含字符‘0’&#xff08;代表海水&#xff09;和 ‘1’&#xff08;代表陆地&#xff09;的二维数组&#xff0c;地图之外可以视作全部是海水&#xff0c;每个岛屿由在上/下/左/右四个方向上相…

python编程从入门到实践答案二

python编程从入门到实践 第五章 if语句1.条件测试&#xff1a;2.更多的条件测试&#xff1a;3.外星人颜色#1&#xff1a;4. 外星人颜色#2&#xff1a;5. 外星人颜色#3&#xff1a;6. 人生的不同阶段&#xff1a;7. 喜欢的水果&#xff1a;8. 以特殊方式跟管理员打招呼&#xff…

UE5.1_TimeLine

UE5.1_TimeLine 问题引入&#xff1a;UE的Timeline可以在一个场景下无限制的使用多少次&#xff1f;一个动画流程的Timeline的时间持续怎么算?TimeLine中嵌套Timeline的做法是否是合理的&#xff1f;

刷题DAY17 | LeetCode 110-平衡二叉树 257-二叉树的所有路径 404-左叶子之和

110 平衡二叉树&#xff08;easy&#xff09; 给定一个二叉树&#xff0c;判断它是否是平衡二叉树。 &#xff08;平衡二叉树 是指该树所有节点的左右子树的深度相差不超过 1。&#xff09; 思路&#xff1a;递归法&#xff0c;用返回值为-1来表示非平衡二叉树 递归三步曲分…

git搜索历史上曾经的文本内容

文章目录 问题在命令行搜索历史内容参考 问题 我们知道&#xff0c;github有文本搜索功能&#xff1a; 比如想搜哪些文件内容包括 aaa &#xff0c;在搜索框中输入 aaa &#xff1a; 但是&#xff0c;如果是历史上曾经有过的文本&#xff0c;这个办法貌似不行。 比如文件 tes…