初学vue(全家桶)-第2天(vue2):数据代理、事件处理

初学vue

1、数据代理

1.1 Object.defineProperty方法

语法:Object.defineProperty(obj, prop, descriptor);
参数说明:
1、obj:目标对象(必需)
2、prop:需定义或者修改的属性的名字(必需)
3、descriptor:目标属性所拥有的特性(必需)

返回值:所传入的函数的对象,及方法中的第一个参数obj
作用:修改属性的特性。
并且该方法设置的属性是不可枚举的,但可以通过enumerable特性修改成可枚举的。

<!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">
    <script src="../JS/vue.js"></script>
    <title>Document</title>
</head>
<body>
    <script>
        const person = {
            name:"张三",
            gender:"男"
        }

        Object.defineProperty(person,"age",{
            value:18,
            // 设置为可枚举属性
            // enumerable:true,
            // 设置属性值是否可以修改
            // writable:true,
            // 设置属性是否可以删除
            // configurable:true,
			
        })

        // console.log(person);
        // 将标签中的属性列出来,并且前面加上索引
console.log(Object.keys(person));
    </script>
</body>
</html>
// 当defineProperty未设置可枚举时
console.log(person);

结果如下:
在这里插入图片描述

// 当defineProperty设置为可枚举时
console.log(person);

结果如下:
在这里插入图片描述
经过defineProperty设置的属性并不能进行值的修改,需要修改对应属性的特性writable的值为true,这样才可以修改值;经过defineProperty设置的属性并不能进行属性的删除,需要修改对应属性的configurable的值为true。

  • defineProperty()方法的高级特性:getter函数和setter函数

描述:定义一个全局变量,定义一个对象person,在这个person对象中有一个属性age的属性值是这个全局变量,要求当修改number变量的值时,这个age对应的值同时可以发生改变,使得不需要手动重新让number的值赋值给age。那么这里可以使用getter和setter函数,如下:

<!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">
    <script src="../JS/vue.js"></script>
    <title>Document</title>
</head>
<body>
    <script>
        let number=18;
        const person = {
            name:"张三",
            gender:"男"
        }

        Object.defineProperty(person,"age",{
            // 其他特性这里省略......

            // 当读取age属性时,get函数(getter)就会被调用,且返回的是age的值
            get:function(){
                console.log("有人读取age属性了");
                return number;
            },
            // 当修改age属性时,set函数(setter)就会被调用,切回收到修改的具体值
            
            set:function(value){
                console.log("有人修改age属性了,且修改的值为:",value);
                number = value;
            }
        });

    </script>
</body>
</html>

在这里插入图片描述

1.2 数据代理

1.2.1 定义

数据代理:通过一个对象代理对另一个对象中属性的操作(读/写)
例如:

<body>
    <script>
        let obj1 = {x:100};
        let obj2 = {y:200};

        Object.defineProperty(obj2,"x",{
            get(){
                return obj1.x;
            },
            set(value){
                obj1.x = value;
            }
        });
    </script>
</body>

上面例题就是简单的数据代理,通过defineProperty()方法中的get和set函数,修该obj2中的x的值以此来修改obj1中x的值。
在这里插入图片描述

1.2.2 简单应用与分析

如下

<!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>Document</title>
    <script src="../JS/vue.js"></script>
</head>
<body>
    <div id="root">
        <h2>姓名:{{name}}</h2>
        <h2>居住地址:{{address}}</h2>
    </div>
    <script>
        const vm = new Vue({
            el:"#root",
            data:{
                name:"小明",
                address:"江苏苏州"
            }
        });
    </script>
</body>
</html>

当控制台输出vm对象时,发现vm中有name和address两个属性,但其实这两个属性应该是在data中的,为什么这里会显示出来呢,这就与数据代理有关了。

在这里插入图片描述


数据代理通过getter和setter方法实现,如下图所示

在这里插入图片描述


vm实例中的属性name是通过getter函数获取到了data中的name。可以通过vm.name获取到值,但不能通过vm.data.namedata.name获取,因为这里的data是局部的,并不是全局的
在这里插入图片描述

验证:将代码中name属性的值修改,可以发现页面控制台、页面中对应的值也修改了。

vm实例可以通过setter函数修改data中的name值。
在这里插入图片描述


那么问题来了,vm对象中的name和address属性是存在与data中的,而且不能通过data.name或者vm.data.name的方式获取到值,那么,为什么却可以通过vm.name的方式取到值和设置值呢?

在vm对象中,有这么一个属性_data
在这里插入图片描述
这个_data属性中存储了data中各个属性的的值,vm可以通过get函数从_data中拿到data中name和address的值,然后赋值给vm中的name和address,从而可以通过对象名.属性名的方式直接拿到值。
并且在使用set函数设置值的时候,也是将重新设置的值放到_data中。关于为什么_data中可以存储data中的值,这就与数据劫持有关了,这里不做多余介绍。


怎么验证vm._data===data?
在这里插入图片描述>
这里data并不存在在全局中,而是在vm对象中。
可以通过将data中的数据从vm对象中抽取成一个对象,如下:

<body>
    <div id="root">
        <h2>姓名:{{name}}</h2>
        <h2>居住地址:{{address}}</h2>
    </div>
    <script>
        let data={
            name:"小明",
            address:"江苏苏州"
        }
        const vm = new Vue({
            el:"#root",
            data:data
        });
    </script> 
</body>

然后比较vm._data===data,vue的配置对象是options,这里的options.data=data,options中的data指的就是定义在vm外部的变量data。
在这里插入图片描述


总结下来,获取、修改对象属性值的方式有
(1)vm.属性名
(2)vm._data.属性名
如果通过vm._data.属性名的形式获取值明显复杂点,而通过数据代理,使用vm.属性名的方式来获取值更加方便简单。
回到代码中
在这里插入图片描述
这是通过Object.defineProperty()方法实现的,如图:在这里插入图片描述
上面两根线代表使用了defineproperty方法在vm对象中定义了name和address属性。

总结:
(1)vue中的数据代理就是通过vm对象来代理data对象中属性的操作(读/写)
(2)vue中数据代理的好处就是能够更见方便简单的操作data中的数据,例如:vm._data.name ⇒ 简写成vm.name
(3)基本原理就是:通过Object.defineProperty()方法把对象中所有的属性添加到vm上。为每一个添加到vm上的属性都指定一个getter和setter,在getter/setter内部去操纵data中对应的属性。

2、事件处理

2.1 基本使用介绍

(1)使用v-on:xxx或者 @xxx 绑定事件,xxx是指事件名

<body>
    <div id="root">
        <h2>欢迎您,{{name}}</h2>
        <button v-on:click="sayHi">点我说Hi</button>
        <!-- 使用 @事件名 的形式 -->
        <button @click="sayHi">点我也说Hi</button>

        <!-- 方法名后面的()想传参就加上,不传参就别加了-->
    </div>
    <script>
        const vm = new Vue({
            el:"#root",
            data:{
                name:"小明"
            },
            methods:{
                sayHi(){
                    alert("Hi!!!")
                }
            }
        });
    </script>

(2)当往函数中添加任意个形参时,第一个形参代表event对象,其他形参的输出undefined。

<body>
    <div id="root">
        <h2>欢迎您,{{name}}!!!</h2>
        <button v-on:click="sayHi">点我说Hi</button>
        <button v-on:click="sayGoodBye">点我说goodBye</button>
    </div>
    <script>
        Vue.config.productionTip = false;

        const vm = new Vue({
            el:"#root",
            data:{
                name:"小明"
            },
            methods:{ 
                sayHi(a,b,c,d){
                    console.log("Hi")
                    console.log(a,b,c,d); 
                },
                sayGoodBye(event){
                    console.log("goodBye");
                    console.log(event);
                }
            }
        });
    </script>
</body>

在这里插入图片描述

(3)事件的回调需要配置在methods对象中,并且这个回调最终会在出现在vm实例上。
在这里插入图片描述

并且methods中的方法并不会数据代理,以内方法不需要展示在页面上。

(4)methods中配置的函数最好不要使用箭头函数,避免this不是指向vm实例。
(5)methods中配置的函数都是被Vue所管理的函数,this指向的是vm或者组件实例对象

<body>
    <div id="root">
        <button v-on:click="clickMe">点我啊</button>
        <button v-on:click="clickMeTo">点我呀</button>
    </div>
    <script>
        Vue.config.productionTip = false;
        const vm = new Vue({
            el:"#root",
            data:{
                name:"小明",
            },
            methods:{
                clickMe(){
                    console.log("你怎么敢点我啊");
                    console.log(this === vm); // true
                    console.log(this); // vm
                },
                clickMeTo:(event) => {
                    console.log("你怎么也敢点我呀");
                    console.log(this); // window
                }
            }
        });
    </script>
</body>

在这里插入图片描述
(5)可以在事件函数中传入参数

<body>
    <div id="root">
        <button v-on:click="clickMe">点我啊</button>
        <button v-on:click="clickMeTo(666)">点我呀</button>
        <!-- clickMeTo传入了一个数字 -->
    </div>
    <script>
        Vue.config.productionTip = false;
        const vm = new Vue({
            el:"#root",
            data:{
                name:"小明",
            },
            methods:{
                clickMe(){
                    console.log("你怎么敢点我啊");
                    
                },
                clickMeTo(number){
                    console.log("你怎么也敢点我呀");
                    console.log(number);
                }
            }
        });
    </script>
</body>

这么做会忽略掉event对象,所以可以在事件中使用占位符$event来表示event,如下:

<body>
    <div id="root">
        <button v-on:click="clickMe">点我啊</button>
        <button v-on:click="clickMeTo(666,$event)">点我呀</button>
        <!-- clickMeTo传入了一个数字 -->
    </div>
    <script>
        Vue.config.productionTip = false;
        const vm = new Vue({
            el:"#root",
            data:{
                name:"小明",
            },
            methods:{
                clickMe(){
                    console.log("你怎么敢点我啊");
                    
                },
                // 这里的a代表了event
                clickMeTo(number,a){
                    console.log("你怎么也敢点我呀");
                    console.log(number,a);
                }
            }
        });
    </script>
</body>

在这里插入图片描述

2.2 事件修饰符

vue中常用事件修饰符:

1、prevent:阻止默认事件
2、stop:阻止事件冒泡
3、once:事件只触发一次
4、capture:使用事件的捕获模式
5、self:只有event.target是当前操作的元素时才触发事件
6、passive:事件的默认行为立即执行,无需等待事件回调执行完毕

实例
  • 阻止默认事件
<body>
    <div id="root">
        <h2>请点击连接</h2>
        <a href="http://www.baidu.com" @click.prevent="showInfo">百度一下</a>
    </div>
    <script>
        const vm = new Vue({
            el:"#root",
            data:{
                name:"小明"
            },
            methods:{
                showInfo(){
                    console.log("你好");
                }
            }
        });
    </script>
</body>

结果:超链接不会跳转,阻止了默认事件,也就是点击连接后不会进行默认跳转。

  • 阻止事件冒泡
<body>
    <div id="root">
        <h2>阻止事件冒泡</h2>
        <div class="demo01" @click="showInfo">
            <button @click.stop="showInfo">点我提示信息</button>
		<!-- 如果不加stop,那么受事件冒泡影响将会出现两次弹窗 -->   
        </div>
    </div>
    <script>
        const vm = new Vue({
            el:"#root",
            data:{
                name:"test"
            },
            methods:{
                showInfo(){
                    alert("弹窗!!!");
                }
            }
        });
    </script>
</body>
  • 事件只触发一次
<body>
    <div id="root">
        <button class="first" @click.once="showInfo">点我触发事件</button>
    </div>
</body>
<script>
    const vm = new Vue({
        el:"#root",
        data:{

        },
        methods:{
            showInfo(){
                alert("这是一次弹窗!");
            }
        }
    });
</script>
  • 事件捕获阶段触发事件
<body>
    <div id="root">
        <div class="outer" @click.capture="showMsg('outer')">
                outer
            <div class="inner" @click="showMsg('inner')">
                inner
            </div>
        </div>
    </div>

    <script>
        new Vue({
            el:"#root",
            data:{},
            methods:{
                showMsg(position){
                    console.log(position,"触发了事件");
                }
            }
        });
    </script>
</body>

经过捕获后的结果就是先触发outer,再触发inner。如果没有被捕获就是先触发inner,后触发outer。
捕获和冒泡的演示如下图:
在这里插入图片描述

  • event.target是当前操作的元素时才触发事件
<body>
	<style>
        div{
            height: 50px;
            background-color:yellow;
        }
    </style>
    <div id="root" @click.self="showInfo">
        <button @click="showInfo">点我触发事件</button>
    </div>

    <script>
        new Vue({
            el:"#root",
            data:{},
            methods:{
                showInfo(){
                    alert("触发事件");
                }
            }
        });
    </script>
</body>

分析,不给父标签加.self时,会受冒泡影响触发两次事件,而在父标签使用事件中使用了.self修饰后,发现点击按钮时,只触发一次,**原因是div上的事件被.self修饰后,只有当event.target是这个div元素时才会触发事件,(也就是直接点击div元素区域时才会触发这个事件),而这里点击按钮触发的事件也只是按钮上绑定的事件触发的。这也可以作为阻止冒泡的方式
在这里插入图片描述

2.3 键盘事件

  • vue中常用的按键别名:

回车:enter
删除:delete(捕获”删除“和“退格”键)
空格:space
退出:esc
换行:tab(特殊:必需配合keyDown使用)
上:up
下:down
左:left
右:right

<body>
    <div id="root">
        <label for="input">请输入:</label>
        <!-- @keyup.enter表示按回车键抬起后触发事件 -->
        <input type="text" id="input" @keyup.enter="showInfo">
    </div>
    <script>
        new Vue({
            el:"#root",
            data:{},
            methods:{
                // 设置回车后输出值的第一种方式
                // showInfo(e){
                //     if(e.keyCode !== 13) return ; // 13是enter键的编码,keyCode可以获取到键盘按键的编码
                //     console.log(e.target.value); // 获取输入框中输入的值
                // }

                // 在键盘事件后使用键的别名
                showInfo(e){
                    console.log(e.target.value);
                }
            }
        });
    </script>
</body>
</html>
  • key和keyCode的区别:
    key获取键的名字,keyCode获取按键的编码
<body>
    <div id="root">
        <label for="input">请输入:</label>
        <input type="text" id="input" @keyup="showInfo">
        <!-- 按键抬起时触发事件 -->
    </div>
    <script>
        new Vue({
            el:"#root",
            data:{},
            methods:{
                // 在键盘事件后使用键的别名
                showInfo(e){
                    // console.log(e.target.value);
                    console.log(e.key,e.keyCode);
                }
            }
        });
    </script>
</body>

在这里插入图片描述

  • vue没有提供别名的按键可以使用按键原始的key值去绑定,但要注意字母转为大小写,不同意思单词之间用-分开,例如转换大小的键caps lock写成caps-lock

  • 系统修饰键(用法特殊)
    如ctrl、alt、shift、meta(window)
    (1)配合keyup使用:按下修饰键的同时,再按下其他键,随后释放其他键,事件才被触发,例如按下ctrl键,同时按下y键,然后释放y键,事件被触发。
    (2)配合keydown使用:能够正常出发事件。

  • Vue.config.keyCodes.自定义键名=键码,自定义按键别名(不推荐)

<body>
    <div id="root">
        <label for="input">请输入:</label>
        <!-- <input type="text" id="input" @keyup.13="showInfo"> -->
        <input type="text" id="input" @keyup.huiChe="showInfo">
    </div>
    <script>
        // 自定义按键别名
        Vue.config.keyCodes.huiChe = 13;

        new Vue({
            el:"#root",
            data:{},
            methods:{
                showInfo(e){
                    console.log(e.target.value);
                }
            }
        });
    </script>
</body>
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值