0. 我有话说
这一章涉及了 Vue 底层的一些原理,学完以后受益匪浅,对我理解 Vue 框架算是又有了一些帮助。(^▽^)
1. 数据代理
1.1 Object.defineProperty
Object.defineProperty可以传入多个配置项,如 value、enumerable、writable, 还有一个高级配置项get函数。Object.defineProperty增加的属性不参与遍历,如果想要参与遍历要修改defineProperty的属性
enumerable控制属性是否可以枚举,默认为falsewritable控制属性是否可以被修改,默认为false
let person = {
name:'张三',
sex: '男'
}
Object.defineProperty(persion, 'age',{
value: 18,
enumerable: true, // 控制属性是否可以枚举,默认为false
writable: true, //控制属性是否可以被修改,默认为false
})
console.log(Object.keys(person)) // ['张三','男'](这是因为Object.defineProperty增加的属性不参与遍历,如果想要参与遍历要修改defineProperty的属性)
- get 函数
- 当有人读取person的age属性时,get函数(getter)就会被调用,且返回值就是age的值
- 应用场景:age属性使用了number,如果不使用getter,当number发生改变时age不会改变,如果使用了getter,则age会随着number的改变而改变
let person = {
name:'张三',
sex: '男'
}
let number = 18
Object.defineProperty(persion, 'age',{
value: 18,
enumerable: true, // 控制属性是否可以枚举,默认为false
writable: true, //控制属性是否可以被修改,默认为false
// 当有人读取person的age属性时,get函数(getter)就会被调用,且返回值就是age的值
get(){
return number
}
})
- set函数
- 当有人修改person的age属性时,set函数(setter)就会被调用,且会收到修改的具体值
let person = {
name:'张三',
sex: '男'
}
let number = 18
Object.defineProperty(persion, 'age',{
value: 18,
enumerable: true, // 控制属性是否可以枚举,默认为false
writable: true, //控制属性是否可以被修改,默认为false
// 当有人读取person的age属性时,get函数(getter)就会被调用,且返回值就是age的值
get(){
return number
}
set(value){
number = value
console.log('有人改变了value的值')
}
})
1.2 数据代理
通过一个对象代理对另一个对象中属性的操作(读/写)
let obj = {x:100}
let obj2 = { y: 200}
Object.defineProperty(obj2, 'x',{
// 当有人读取person的age属性时,get函数(getter)就会被调用,且返回值就是age的值
get(){
return obj.x
}
set(value){
obj.x = value
console.log('有人改变了obj2的x值')
}
})
1.3 Vue中的数据代理
- Vue实例中的data属性都是通过 Object.defineProperty加上的,他们都有自己的setter和getter,这里就是使用Vue的实例 vm 对data进行数据代理
- Vue中数据代理的好处: 更加方便的操作data中的数据
- 基本原理:
- 通过
Object.defineProperty()把data对象中所有属性添加到vm上 - 为每一个添加到vm上的属性,都指定一个getter/setter
- 在getter/setter内部去操作(读 / 写)data中对应的属性
const vm = new Vue({
data(){
// 此处的this是Vue实例对象,
// 但是data必须是普通函数,不能是箭头函数,箭头函数的this是window
retrn {
name: 'mike'
}
} // Vue实例传入的这个对象叫做options
)
vm.name = 'tom' // 修改了vm的name属性,调用了set方法,更改了data中的name,页面里使用的该数据也会改变
vm._data就是里面的data
2. 事件处理
2.1 绑定监听
-
使用 v-on:xxx="fun", @xxx="fun"绑定事件
-
事件的回调需配置在methods对象中,最终会在vm上;
-
methods中配置的函数,不要用箭头函数,否则this就不是vm了;
-
methos中配置的函数,都是被Vue所管理的函数,this的指向是vm 或 组件实例
-
@click = "demo" 和 @click="demo($event)"效果一致,但后者可以传参
-
默认事件形参: event
- event.target 表示触发事件的元素
- 该事件内部的 this指向 Vue 的实例 vm
- 隐含属性对象: $event
2.2 事件修饰符
prevent:阻止默认事件(常用);event.preventDefault()stop:阻止事件冒泡(常用);event.stopPropagation()once:事件只触发一次(常用);capture:使用事件的捕获模式;self:只有event.target是当前操作的元素时才触发事件;passive:事件的默认行为立即执行,无需等待事件回调执行完毕;(一般执行顺序是 事件触发 -> 回调事件执行 -> 默认行为执行,比如滚动条向下滚动)一般在移动端用的比较多
<!-- 阻止事件冒泡 -->
<div id="big" @click="test">
<div id="small" @click.stop="test2"></div>
</div>
<!-- 提交事件不再重载页面 -->
<form v-on:submit.prevent="onSubmit"></form>
<!-- 点击事件将只会触发一次 -->
<a v-on:click.once="doThis"></a>
<!-- 使用事件的捕获模式,在捕获阶段处理事件,先执行外层元素事件,再执行内部元素事件 -->
<div @click.capture="test">
div1
<div id="small" @click="test2">
div2
</div>
</div>
2.3 键盘事件
- Vue中常用的按键别名:
回车 => enter
删除 => delete (捕获“删除”和“退格”键)
退出 => esc
空格 => space
换行 => tab (特殊,必须配合keydown去使用)
上 => up
下 => down
左 => left
右 => right
-
Vue未提供别名的按键,可以使用按键原始的key值去绑定,但注意要转为kebab-case(短横线命名) -
系统修饰键(用法特殊):
ctrl、alt、shift、meta
- 配合
keyup使用:按下修饰键的同时,再按下其他键,随后释放其他键,事件才被触发。 - 配合
keydown使用:正常触发事件。
-
也可以使用
keyCode去指定具体的按键(不推荐) -
Vue.config.keyCodes.自定义键名 = 键码,可以去定制按键别名
<input type="text" @keyup="showInfo">
showInfo(e){
// 原生js 实现按键回车再触发事件
if(e.keyCode !== 13) return
}
<!-- 任何按键按下都会触发回调函数 -->
<textarea @keyup="testKeyup"></textarea>
<!-- 下面的两种写法效果是一致的 -->
<!-- 使用按键码,回车键的keyCode是13 -->
<textarea @keyup.13="testKeyup"></textarea>
<!-- 使用按键修饰符,因为回车键比较常用,所以vue为他设置了名称,可以直接使用enter来代替 -->
<textarea @keyup.enter="testKeyup"></textarea>
<!-- Vue没有提供别名的按键,使用原始的key,但是要转换为短横线命名,这里举的例子是大小写转换按键 -->
<textarea @keyup.caps-lock="testKeyup"></textarea>