数据响应式原理是我们在开发过程中一定要了解的知识,彻底搞懂数据响应式原理,刻不容缓。
Object.defineProperty()方法
首先我们应该知道Vue采用数据劫持 发布者-订阅者模式实现双向数据绑定, 然后我们来看一下Object.defineProperty()方法,此方法会直接在对象上定义属性,或者修改一个对象的现有属性,并返回此对象
代码语言:javascript复制let obj = {};
Object.defineProperty(obj, 'a', {
value: 1
});
Object.defineProperty(obj, 'b', {
value: 2
});
console.log(obj); //{a:1,b:2}
console.log(obj.a, obj.b); //1,2
Object.defineProperty()方法还可以设置一些额外的隐藏属性。
代码语言:javascript复制Object.defineProperty(obj, 'a', {
value: 1 ,
// 是否可写
writable: false
});
Object.defineProperty(obj, 'b', {
value: 2 ,
// 是否可以被枚举
enumerable: false
});
getter和setter
get
属性的 getter 函数,如果没有 getter,则为 undefined。当访问该属性时,会调用此函数。执行时不传入任何参数,但是会传入 this 对象 (由于继承关系,这里的 this 并不一定是定义该属性的对象)。该函数的返回值会被用作属性的值。默认为 undefined。
set
属性的 setter 函数,如果没有 setter,则为 undefined。当属性值被修改时,会调用此函数。该方法接受一个参数(也就是被赋予的新值) ,会传入赋值时的 this 对象默认为 undefined。
文字有些晦涩难懂,看代码会更好理解些
Object.defineProperty(obj,'a'{
//getter
get(){
console.log("你试图访问obj的a属性');
}
//setter
set() {
console.log("你试图改变obj的a属性');
}
})
console.log(obj.a);
obj.a = 10;
defineReactive函数
getter/setter需要变量转换
代码语言:javascript复制var temp;:
Object.defineProperty(obj,'a',[
get(){
console.log("你试图访问obj的a属性');return temp;
}
set(newValue) {
console.log("你试图改变obj的a属性’,newValue);
temp = newValue;
}
})
但是使用defineReactive函数就不用设置临时变量了,用闭包即可。
代码语言:javascript复制function defineReactive(data,key,val) {
Object.defineProperty(data,key,{
enumerable: true,
configurable:true,
get() {
console.log('访问obj的key属性')
},
set(newVal) {
console.log('改变obj的key属性',)
if(val === newVal) {
return
}
val = newVal
}
})
}
对象响应式处理
通过observe,将一个正常的object转换为每个层级 的属性都是响应式(可以被侦测的)的object
代码语言:javascript复制function Observer(data) {
this.data = data;
this.walk(data);
}
Observer.prototype = {
walk: function(data) {
var self = this;
Object.keys(data).forEach(function(key) {
self.convert(key, data[key]);
});
},
convert: function(key, val) {
this.defineReactive(this.data, key, val);
},
defineReactive: function(data, key, val) {
var dep = new Dep();
var childObj = observe(val);
Object.defineProperty(data, key, {
enumerable: true,
configurable: true,
get: function() {
if (Dep.target) {
dep.depend();
}
return val;
},
set: function(newVal) {
if (val === newVal) {
return;
}
val = newVal;
childObj = observe(newVal);
dep.notify();
}
});
}
};
function observe(value, vm) {
if (!value || typeof value !== 'object') {
return;
}
return new Observer(value);
};
如上代码所示,Observer会对数据进行劫持并且在每个数据属性中创建一个对应的Dep实例。Dep是Vue.js中的一个订阅者列表,用于收集依赖于该对象的所有Watcher对象,在数据变化时执行相应的update回调函数。当一个数据对象被监听后,Observer就会将其属性进行劫持,通过Object.defineProperty将属性改成getter/setter的形式,并当属性被读取时自动收集相应的Watcher对象,在属性变化时自动触发相应的更新逻辑。
Observer是Vue.js变化侦测系统中最为核心和重要的部分。通过Observer监听数据的变化并更新视图,才能实现Vue.js的双向数据绑定和响应式的数据更新机制。
数组的响应式处理
数组的响应式通过重写数组的方法来实现响应式(push,pop,shift,unshift,splice,sort,reverse)
至此数据响应式原理讲解完毕,如有错误,敬请指正。