1. getter / setter
ES6中的get和set语法将对象属性绑定到函数,访问/赋值时函数会被调用。
let obj = {
province: '广东',
city: '广州',
get location(){
return this.province + this.city
},
set location(value){
console.log(value)
this.province = value.substring(0,2)
this.city = value.substring(2)
}
}
console.log(obj.location) //广东广州
obj.location = '广东深圳'
2. Object.defineProperty
Object.defineProperty可以用来给对象添加属性value,添加getter/setter,来对属性的读写进行监控。
先来添加属性value
let data = {}
Object.defineProperty(data, 'n', {value: 1})
console.log(data.n) // 输出1
添加getter / setter, 并对读写进行监控
let data = {}
data._n = 1
Object.defineProperty(data, 'n',{
get(){
return data._n
},
set(value){ //对n的赋值进行监控
if(value > 0){data._n = value}
else {console.log('不可为负')}
}
})
以上代码存在data._n会被修改影响n的值的情况,再改进一下
let data = proxy({ data:{n:0} }) //传入一个匿名对象
function proxy({data}){ //解构赋值
const obj = {}
Object.defineProperty(obj, 'n', {
get(){
return data.n
},
set(value){
if(value > 0){data.n = value}
else {console.log('不可为负')}
}
})
return obj
}
以上代码使用了代理的思路,即为函数中的对象obj,且只暴露代理。
但是以上代码在匿名对象的data属性指向另一个外部对象时仍存在风险,
let myData = {n:0}
let data = proxy({ data:myData })
直接修改myData也会影响n的值
于是再改进一下,对匿名对象的属性的读写也进行监听
let myData = {n:0}
let data = proxy({ data:myData })
function proxy({data}){ //解构赋值
// 对匿名对象的data进行监听
let value = data.n
Object.defineProperty(data, 'n', {
get(){
return value
},
set(value){
f(value > 0){data.n = value}
else {console.log('不可为负')}
}
})
//以下内容不变
const obj = {}
Object.defineProperty(obj, 'n', { //对原有的n属性进行覆盖
get(){
return data.n
},
set(value){
if(value > 0){data.n = value}
else {console.log('不可为负')}
}
})
return obj
}
3. VUE中对data的监听与代理
const vm = Vue({data: myData})
- 代理:vm代理了 data 对象上所有的 property,因此访问
vm.a等价于访问vm.$data.a。 - 监听:Vue 将会递归将 data 的 property 转换为 getter/setter,从而让 data 的 property 能够响应数据变化。
4. data中的对象存在的问题
vue实例不会监听一开始不存在的属性。
new Vue({
data:{
obj:{
a: 0
}
},
template:`
<div>{{obj.b}}</div>
<button @click='setB'>B</button>
`,
methods:{
setB(){
this.obj.b = 1
}
}
}),
点击按钮出发setB方法,为obj添加b属性,但由于初始化时并不存在该属性,所以并未被Vue实例监听与代理,并不会出发重新渲染。
Vue提供了set方法来解决此问题
setB(){
Vue.set(this.obj, 'b', 1)
或
this.$set(this.obj, 'b', 1)
}
Vue.set 和 this.$set 的作用:
- 新增key
- 自动创建代理和监听(如果没有创建过)
- 触发UI更新(但并不会立刻更新)
5. data中的数组存在的问题
new Vue({
data:{
obj:{
array:[1,2,3]
},
template:`
<div>{{array}}</div>
<button @click='setArray'>set</button>
`,
methods:{
setArray(){
this.array[4] = 4
}
}
}),
以上代码无法触发视图更新,原因与对象相似,但数组情况会更复杂一些。
针对数组,VUE篡改了数组的API,提供了7个变更方法 push() pop() shift() unshift() splice() sort() reverse()和替换方法filter() concat() slice()
以上代码,应使用变更方法push()
methods:{
setArray(){
this.array.push(4)
}
}
变更方法的实现思路,大致为在数组的原型链中添加一层,所添加的数组对象包含变更方法。 在ES6中可以使用继承
class VueArray extends Array{
push(...args){
const oldLength = this.length
super.push(...args)
console.log('你push了')
for(let i = oldLength; i<this.length; i++){
Vue.set(this, i ,this[i])
}
}
}
//这不是Vue的真实实现,只是展示下思路
小结
VUE的数据相应式就是VUE实例对data对象的监听和代理操作,当数据变化,vm就会调用render()函数,实时更新UI。