文章目录
- 初学vue
- 1、数据代理
- 1.1 Object.defineProperty方法
- 1.2 数据代理
- 1.2.1 定义
- 1.2.2 简单应用与分析
- 2、事件处理
- 2.1 基本使用介绍
- 2.2 事件修饰符
- vue中常用事件修饰符:
- 实例
- 2.3 键盘事件
语法: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>Documenttitle>
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。
描述:定义一个全局变量,定义一个对象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>Documenttitle>
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>
数据代理:通过一个对象代理对另一个对象中属性的操作(读/写)
例如:
<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的值。
如下
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>Documenttitle>
<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中的,为什么这里会显示出来呢,这就与数据代理有关了。
vm实例中的属性name是通过getter函数获取到了data中的name。可以通过
vm.name
获取到值,但不能通过vm.data.name
和data.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中对应的属性。
(1)使用v-on:xxx或者 @xxx 绑定事件,xxx是指事件名
<body>
<div id="root">
<h2>欢迎您,{{name}}h2>
<button v-on:click="sayHi">点我说Hibutton>
<button @click="sayHi">点我也说Hibutton>
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">点我说Hibutton>
<button v-on:click="sayGoodBye">点我说goodByebutton>
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>
<body>
<div id="root">
<button v-on:click="clickMe">点我啊button>
<button v-on:click="clickMeTo(666)">点我呀button>
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>
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>
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>
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。
捕获和冒泡的演示如下图:
<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元素区域时才会触发这个事件),而这里点击按钮触发的事件也只是按钮上绑定的事件触发的。这也可以作为阻止冒泡的方式。
回车:enter
删除:delete(捕获”删除“和“退格”键)
空格:space
退出:esc
换行:tab(特殊:必需配合keyDown使用)
上:up
下:down
左:left
右:right
<body>
<div id="root">
<label for="input">请输入:label>
<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>
<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.huiChe="showInfo">
div>
<script>
// 自定义按键别名
Vue.config.keyCodes.huiChe = 13;
new Vue({
el:"#root",
data:{},
methods:{
showInfo(e){
console.log(e.target.value);
}
}
});
script>
body>