数据响应式即数据改变,UI页面做出响应进行更新。data 是响应式,当修改 Vue 的构造选项 data 中的属性时,UI页面会做出响应,我们知道 Vue 是通过 Object.defineProperty 来实现数据响应的。那么具体原理是怎样的呢?
Object.defineProperty()
- 给对象定义新属性 value
- 给对象添加 getter/setter
- getter/setter 方法用于对属性的读写进行监控
getter/setter
- 实际上是对伪属性的读写,并不是真实属性
let obj = {
姓: '小',
名: '白',
get 姓名() {
return this.姓 + this.名
},
set 姓名(xxx) {
this.姓 = xxx[0]
this.名 = xxx.slice(1)
}
}
console.log(`姓名:${obj.姓名}`) //姓名:小白
obj.姓名 = '太阳' //触发 set 函数
console.log(`姓:${obj.姓},名:${obj.名}`) //姓:太,名:阳
console.dir(obj) //姓名并不是一个真正的属性,而是使用 getter/setter 方法来模拟操作
实际上,Vue data 中的 xxx 属性在经过了 new Vue()之后也是变成了 xxx:(...)
在Object.defineProperty()中使用 getter/setter
var _n = 0 //用来存储伪属性 n 的值
Object.defineProperty(obj,'n',{
get(){
return _n //不可使用 this.n ,是不存在的
},
set(value){
_n = value
}
})
console.log(`${obj.n}`) //0
obj.n = '5'
console.log(`${obj.n}`) //5
_n = 6
console.log(`${obj.n}`) //6
const vm = new Vue({data:myData})
- 让 vm 成为 myData 的代理 (proxy)
- 对 myData 所有属性进行监控
代理 (proxy)
- 对 myData 对象的属性读写,都由另一个对象 vm 负责。
- 比如 myData.n 不用,使用 vm.n 来操作 myData.n。
例:
let myData = {
n: 0
}
let data2 = proxy({
data: myData
})
function proxy({data}) { //解构赋值
const obj = {}
Object.defineProperty(obj,'n', {
get() {
return data.n
},
set(value) {
if (value > 10) return //监控
data.n = value
}
})
return obj //obj 是 myData 的代理
}
data2.n = 9
console.log(`${data2.n}`) // 9
data2.n = 11
console.log(`${data2.n}`) // 9
myData.n = 11
console.log(`${data2.n}`) // 11! 不是9?
obj 定义了一个伪属性,代理使得 obj 通过 getter/setter 来操作 myData 的值,并且实行了监控。
但是有一个问题,当直接对 myData 的值进行了修改,obj 是无法监测进行操作到的。如以上代码,
就相当于 在 Vue 中,直接修改 myData.n ,绕过了 vm,UI页面也因此没有响应。
那么 如何解决的呢? 给对象添加数据监听
添加数据监听
- 用一个 value 储存 属性值 (data.n)
- 将属性 n 删去
- 使用 Object.defineProperty() 给 对象 data 定义一个伪属性 n
- 实际上这个伪属性 n 是取代了真正的 data.n,第二步操作在代码实践时可直接省略
- 通过 getter/setter 模拟操作 n 来监听 data
监听 data 只需要添加以下代码
let value = data.n
Object.defineProperty(data,
'n', {
get() {
return value
},
set(newValue) {
if (value > 10) return //监控
value = newValue
}
})
总结
- 对 data 数据进行篡改并监听,将属性变成伪属性
- 让 vm 成为 myData 的代理
- 对 myData 的所有属性进行监控
- 修改 vm.n ,UI页面做出响应