Vue源码实现--依赖收集(2)
关于上一篇的几点疑问其实在看源码的过程中已经看明白了,但是回过头来发现又容易忘了,这也是我决定写几篇文章记录一下的原因。
数组的依赖收集:
我们都知道vue中,直接改变一个数组(比如arr[1] = 2)是不会触发页面的更新的,必须调用数组的方法:arr.splice(1,1,2)才可以。
简单说一下vue的实现:其实我们调用的splice方法并不是数组原生的方法,当observer(data)时,如果data是一个数组,那么此时就用事先定义的七个方法(push,pop,shift,unshift,sort,reverse,splice)替换掉原来数组上的方法。
/*Observer Array*/
var initArrayMethod = function (arr) {
var arrayProto = Array.prototype
// 复制原生数组原型上的方法,并替换掉其中的7个
var arrayMethods = Object.create(arrayProto)
;['push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse'].forEach(function(method) {
var origin = arrayMethods[method]
arrayMethods[method] = function () {
// 保存原生数组调用的结果
var result = origin.apply(this, arguments)
// 这里就是调用被观测的属性上的dep(__ob__)
this.__ob__.dep.notify())
return result
}
})
arr.__proto__ = arrayMethods
}
什么时候收集的依赖呢,代码戳这里:childOb
上面的代码中,this.ob.dep.notify()就是为什么需要在被观测的对象中也维护一个dep实例了(ob),对象上的ob主要用在这两个地方:一个就是调用数组方法之后触发订阅者回调,另一个就是vue中调用set,del方法时触发订阅者。
对比源码可以发现,比上面的的代码要多一些,那么有什么不同呢?其实主要是这一行代码:
if (inserted) ob.observeArray(inserted)
ob.observeArray()是对数组中数据进行observe,主要是对数组嵌套对象,数组嵌套数组这种情况进行依赖收集,在这里就是对push,unshift,splice这三个有插入新的数据的方法对新插入的数据observe。
包括在Observer类中,为了使代码简便,在这里我暂时都没对数组嵌套对象的情况进行处理
深观测一个对象:
watch方法的options中,有个deep参数,设置为true的时候,就可以深观测一个对象,其实原理非常简单,就是在依赖收集的时候遍历对象的每一个属性,触发每一个属性的getter,那么每个属性都会被依赖收集
经过上面的处理,现在我们的vue依赖收集多了这些功能:
var app = new Vue({
data: function () {
return {
a: 1,
b: {
c: 2
},
arr: [1,2,3]
}
}
})
app.$watch('a', function () {
console.log('a is change')
})
app.$watch('arr', function () {
console.log('arr is change')
})
app.$watch('b', function () {
console.log('b is change')
}, {deep: true})
var test = app.arr.splice(0,1,11)
app.b.c = 3
返回结果为:
arr is change
b is change
附上本文的完整代码:附件