Appearance
数组响应式
数组变更方法的响应式
javascript
// 当对象中 value 是数组时,要实现响应式,只需要在变更方法内部调用 dep.notify 通知
// render watcher 更新即可,但 value 并没有收集 render watcher,而 key 会收集
// render watcher,我们需要让 value 也有 dep,因此在 Observer 类的构造器中
// this.dep = new Dep(),进入这里的 value 只可能是数组或对象,对象我们也给它 dep,
// 因为之后 Vue.set() 要用到。
// 如何让 ob.dep 收集 watcher,由于 key 会收集 render watcher,在
// defineReactive(obj, key, value) 中,因为 value 可能是数组或对象,需要实现数组或
// 对象所有层次的对象的属性的劫持,会 observe(value),而 observe 会返回 ob,那么只有
// value 是数组或对象时 ob 才为真,通过 childOb 接收这个 ob,key 通过 dep.depend()
// 收集 render watcher,收集完后我们判断下 childOb 是否为真,为真说明 key 对应的 value
// 是数组或对象,通过 childOb.dep.depend() 完成 value 收集 render watcher,这样当
// 调用变更方法时,内部 this.__ob__.dep.notify() 就能实现数组变更方法的响应式更新
class Observer {
constructor (val) {
// 给数组或对象 添加一个 dep 属性
// 作用1:Vue.set() 实现响应式
// 作用2:数组变更方法实现响应式
this.dep = new Dep
}
}
arrayMethods[method] = function (...args) {
const ob = this.__ob__ // 获取ob
// ...
ob.dep.notify() // 通知 render watcher 更新
}
function defineReactive(
obj: object,
key: string,
val?: any
) {
// 每个属性收集使用它们的 watcher
const dep = new Dep()
// 需要实现数组或对象所有层次的对象的属性的劫持
// childOb 是 Observe 实例, val 是数组或对象时 childOb 才有值
let childOb = observe(val, false, mock)
// 为该属性实现响应式
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function reactiveGetter() {
const value = val
// 如果模板外使用数据,不进行收集操作。
if (Dep.target) {
// 互相收集
dep.depend()
// value 是数组或对象
if (childOb) {
// 数组变更方法响应式 & Vue.set()
childOb.dep.depend()
}
}
}
}
}数组嵌套的响应式
javascript
// 外层数组 dep 中收集了 render watcher, 变更方法中通过 ob.dep.notify() 能更新视图,
// 内层数组, observer(value) 时会产生它的 dep,然后走 observeArray(),不会进入
// defineReactive 中,这也就是为什么数组里只有对象才会被劫持。因此访问内层数组时不触发
// get,内层数组的 dep 收集不到任何 render watcher,变更方法中 ob.dep.notify()不能
// 更新视图。但外层数组是对象的属性,在外层数组的 dep 收集 render watcher 时,如果
// value 是数组,调用 dependArray 方法让这个数组内部的数组收集 render watcher,
// dependArray 遍历数组每一项 item,如果某项有 __ob__ 属性,就 item.__ob__.dep.depend()
// 收集 watcher,由于还可能再嵌套,如果 item 是数组,递归调用 dependArray 让所有层次
// 的数组都收集到 render watcher,这样内层数组调用变更方法时就能实现视图的更新了。
function defineReactive(
obj: object,
key: string,
val?: any
) {
// 每个属性收集使用它们的 watcher
const dep = new Dep()
// 需要实现数组或对象所有层次的对象的属性的劫持
// childOb 是 Observe 实例, val 是数组或对象时 childOb 才有值
let childOb = observe(val, false, mock)
// 为该属性实现响应式
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function reactiveGetter() {
const value = val
// 如果模板外使用数据,不进行收集操作。
if (Dep.target) {
// 互相收集
dep.depend()
// value 是数组或对象
if (childOb) {
// 数组变更方法响应式 & Vue.set() 所有层次属性的响应式
childOb.dep.depend()
// 数组嵌套时内部数组变更方法响应式
if (isArray(value)) {
dependArray(value)
}
}
}
}
})
}
function dependArray(value: Array<any>) {
for (let e, i = 0, l = value.length; i < l; i++) {
e = value[i]
if (e && e.__ob__) {
e.__ob__.dep.depend()
}
if (isArray(e)) {
dependArray(e)
}
}
}