稍微学一下Vue的数据响应式(Vue2及Vue3区别)

2020-06-12 20:51:33易采站长站整理

return;
}
let ob;
if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) {
ob = value.__ob__;
} else {
ob = new Observer(val);
}
return ob;
}

函数中判断如果是对象则递归调用 Observer 来实现所有属性的变化侦测,根据 __ob__ 属性判断是否已处理过,防止多次重复处理,Observer 处理过后会给数据添加这个属性,下面写一个对象试一下:


const people = {
name: 'c',
age: 12,
parents: {
dad: 'a',
mom: 'b'
},
mates: ['d', 'e']};
new Observer(people);
people.name; // getter c
people.age++; // getter 12 setter 13
people.parents.dad; // getter {} getter a

打印 people 可以看到所有属性添加了 getter/setter 方法,读取 name 属性时打印了 people.age++ 修改 age 时打印了 getter 12 setter 13 说明 people 的属性已经被全部成功代理监听。

Array 的变化侦测

可以看到前面 Observer 中仅对 Object 类型个数据做了处理,为每个属性添加了 getter/setter,处理后如果属性值中有数组,通过 属性名 + 索引 的方式(如:this.people.mates[0])获取也是会触发 getter 的。但是如果通过数组原型方法修改数组的值,如 this.people.mates.push(‘f’),这样是无法通过 setter 侦测到的,因此,在 Observer 中需要对 Object 和 Array 分别进行单独的处理。

为侦测到数组原型方法的操作,Vue 中是通过创建一个拦截器 arrayMethods,并将拦截器重新挂载到数组的原型对象上。

下面是拦截器的定义:


const ArrayProto = Array.prototype;
const arrayMethods = Object.create(ArrayProto);
;[
'push',
'pop',
'shift',
'unshift',
'splice',
'sort',
'reverse'
].forEach(method => {
const original = ArrayProto[method];
Object.defineProperty(arrayMethods, method, {
value: function mutator(...args) {
console.log('mutator:', this, args);
return original.apply(this, args);
},
enumerable: false,
writable: true,
configurable: true
})
})

这里 arrayMethods 继承了 Array 的原型对象 Array.prototype,并给它添加了 push pop shift unshift splice sort reverse 这些方法,因为数组是可以通过这些方法进行修改的。添加的 push pop… 方法中重新调用 original(缓存的数组原型方法),这样就不会影响数组本身的操作。

最后给 Observer 中添加数组的修改:直接将拦截器挂载到数组原型对象上


class Observer {
constructor(value) {
this.value = value;
def(value, '__ob__', this);
if (Array.isArray(value)) {
value.__proto__ = arrayMethods;