if (inserted) ob.observeArray(inserted) // notify change ob.dep.notify()
将其转换为响应式数据,并通过 ob.dep.notify 来调用观察者的方法,而这里的观察者列表就是通过上述的 childOb.dep.depend 来收集的。同样的,为了实现对象新增数据的响应式,我们需要提供相应的 hack 方法,而这就是我们常用的 Vue.set/Vue.delete 。
// src/core/observer/index.js
export function set (target: Array<any> | Object, key: any, val: any): any {
...
if (Array.isArray(target) && isValidArrayIndex(key)) {
target.length = Math.max(target.length, key)
target.splice(key, 1, val)
return val
}
if (key in target && !(key in Object.prototype)) {
target[key] = val
return val
}
const ob = (target: any).__ob__
if (target._isVue || (ob && ob.vmCount)) {
process.env.NODE_ENV !== 'production' && warn(
'Avoid adding reactive properties to a Vue instance or its root $data ' +
'at runtime - declare it upfront in the data option.'
)
return val
}
if (!ob) {
target[key] = val
return val
}
defineReactive(ob.value, key, val)
ob.dep.notify()
return val
}
- 判断value是否为数组,如果是,直接调用已经hack过的splice即可。
- 是否已存在key,有的话说明已经是响应式了,直接修改即可。
- 接着判断target.__ob__是否存在,如果没有说明该对象无须深度观察,设置返回当前的值。
- 最后,通过defineReactive来设置新增的key,并调用ob.dep.notify通知到观察者。
现在我们了解了 childOb.dep.depend() 是为了将当前 watcher 收集到 childOb.dep ,以便在增、删数据时能通知到 watcher 。而在 childOb.dep.depend() 之后还有:
if (Array.isArray(value)) {
dependArray(value)
}
/**
* Collect dependencies on array elements when the array is touched, since
* we cannot intercept array element access like property getters.
*/
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)
}
}
}
在触发 target[key] 的 getter 时,如果 value 的类型为数组,则递归将其每个元素都调用 __ob__.dep.depend ,这是因为无法拦截数组元素的 getter ,所以将当前 watcher 收集到数组下的所有 __ob__.dep ,这样当其中一个元素触发增、删操作时能通知到观察者。比如:
const data = {
list: [[{value: 0}]],
};
data.list[0].push({value: 1});
这样在 data.list[0].__ob__.notify 时,才能通知到 watcher 。
target[key] 的 getter 主要作用:
将 Dep.target 收集到闭包中 dep 的观察者列表,以便在 target[key] 的 setter 修改数据时通知观察者
