Vue中 响应式数据绑定系统
Vue中双向数据绑定事项采用了数据劫持的方式,结合发布订阅模式,其中核心方法是Object.defineProperty(),通过劫持各个属性的setter和getter方法,来做到实时响应数据的目的。
复制代码
1. 认识Object.defineProperty()
该方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回这个对象。
Object.defineProperty(obj, prop, descriptor)
obj: 需要定义的对象
prop:定义的属性名称
descriptor: 描述属性的内容
其中描述属性有很多配置项
复制代码
Object.defineProperty(obj,"test",{
configurable:true | false,//是否可以删除目标属性或是否可以再次修改属性的特性,默认false
enumerable:true | false, //属性是否可以被枚举(使用for...in或Object.keys()),默认false
value:任意类型的值,//属性对应的值,可以使任意类型的值,默认为undefined
writable:true | false //属性的值是否可以被重写,默认false
});
复制代码
1.1. 简单来个例子:
let obj = {}
let val
Object.defineProperty(obj, 'test',{
get(){
console.log('取值了')
return val
},
set(newVal){
console.log('赋值了')
val = newVal
}
})
//obj.test 触发get方法,obj.test = 1触发set方法
复制代码
1.2. 稍微完善例子:
上面的例子加入对象的属性有多层,比如obj={test:{test:1}},里面的test就监听不到了
为此我们来完善一下
复制代码
let obj = {name:'jeffywin',play:{sport:'basketball'}}
function observer(obj){
if(typeof obj !== 'object') return; //如果不是对象,则return
for(let key in obj) {
defineReactive(obj, key, obj[key]) // 对象,属性,内容
observer(obj[key])//假如最后一层不是对象,把obj[key]递归下去
}
}
function defineReactive(tar, por, val) {
Object.defineProperty(tar, por,{
get() {
return val
},
set(newVal) {
if(val === newVal) return
val = newVal
}
})
}
observer(obj)//通过递归来拿到多层嵌套内容
复制代码
2. ES6 proxy代理器
var proxy = new Proxy(target, handler)
复制代码
通过构造函数生成proxy,target参数是要拦截的目标对象,handler参数也是一个对象,用来定制拦截行为
2.1简单例子
let obj = {name: 'jeffywin'}
let proxy = new Proxy(obj,{
get: function(target,key){
console.log('取值')
return target[key]
},
set: function(target,key,value){
target[key] = value
}
})
proxy.name 调用get方法,proxy.name = 'jordan' 调用set方法
复制代码
2.2简单例子完善(处理私有变量)
let obj = {_secretAge:18, _secretPlay:'play', version: 1.0}
let Api = new Proxy(obj,{
get: function(target,key){
if(key.startsWith('_')) {
console.log('私有变量不能被访问')
return false;
}
return target[key]
},
set: function(target,key,value) {
if(key.startsWith('_')) {
console.log('私有变量不能被修改')
return false;
}
target[key] = value
}
})
复制代码