Appearance
前文讲述了 Object 的依赖收集和触发,那么在 Array 中,同之前的方式相比,有哪些变化呢?
收集依赖:在 getter 中
触发依赖:在重写操作数组的方法中 (也就是 arrayMethods)
为什么说收集依赖也在 getter 中呢?这里不是操作 Object 的吗?
js
new vue({
data(){
return {
arr: [1,2,3,4]
}
}
})
我们对 data return 出来的这个对象转换成响应式进行观测;我们获取数组时,肯定是 obj.arr;这样的话,这个数组肯定会走进 obj 的 getter 中,所以我们收集依赖也还是在 getter 中的。
- 地址:
src/core/observer/index.js
:
js
export class Observer {
constructor (value) {
this.value = value
// 创建一个依赖管理器,用来收集数组依赖
this.dep = new Dep()
if (Array.isArray(value)) {
} else {
this.walk(value)
}
}
}
//在对象上定义反应属性
export function defineReactive (
obj: Object, //要响应的对象
key: string, // 响应对象的键
val: any, // 对象的值
) {
// 递归,针对子对象设置geter和setter,并返回子对象的Observer实例
let childOb = observe(val)
Object.defineProperty(obj, key, {
enumerable: true, //表示能否通过for in 循环属性
configurable: true, //是否可以删除或重新定义属性
// 在这里可以知道获取了值
get: function reactiveGetter () {
if (Dep.target) {
if (childOb) {
// 子对象进行依赖收集
childOb.dep.depend()
// 如果是数组,对每个成员都进行依赖收集,如果数组成员还是数组则递归;例如二维数组
if (Array.isArray(val)) {
dependArray(val)
}
}
return val
},
// 在这里可以知道更改了值
set: function reactiveSetter (newVal) {
dep.notify() // 通知所有依赖这个对象观察者进行更新
val=newVal
}
})
}
// __ob__是否转换成响应式了
function dependArray (value: Array<any>) {
for (let e, i = 0, l = value.length; i < l; i++) {
e = value[i]
e && e.__ob__ && e.__ob__.dep.depend()
if (Array.isArray(e)) {
dependArray(e)
}
}
}
再举个例子:
js
new vue({
data(){
return{
arr: [1,2,3,4]
}
}
})
分析一下整体流程:
我们
new Observer()
的时候,会进去到 defineReactive 这个函数中,执行了 observe(val) 获取到了 Observer 实例对象,并给该对象设置了 getter 和 setter (注意:此时observe方法传入的val是数组arr
)当调用该对象的 getter 的时候,我们对数组进行依赖收集,如果子对象中还有数组,则进行递归收集。
通知依赖进行更新:
js
methodsToPatch.forEach(function (method) {
def(arrayMethods, method, function mutator (...args) {
const result = original.apply(this, args)
// __ob__存的是Observer实例
const ob = this.__ob__
// 通知更新
ob.dep.notify()
return result
})
})
此时我们要通知依赖,首先要能够对依赖进行访问,这里的关键在于 this,此时的this 指向的是被响应的数据 value ,数据 value上会绑定一个 __ob__
属性;
__ob__
的值是 Observer 实例对象,我们在这个对象中可以访问到依赖管理器,然后调用 dep.notify
方法就可以去通知依赖了。
当然,也存在不足的地方,就是用下标去更改数据是无法被监测的,也无法用 length = 0
去置空数组。
vue 也提供了 set 方法 和 delete 方法去更改数据