vue2源码学习--06数组的依赖收集和更新

145 阅读3分钟

正如标题,本篇是实现数组的依赖收集以及更新通知,但是实际上我们其实是对数组是做了new Dep并且视图读取的时候进行了依赖收集的,只不过进行数组的更新的方法(如push)的时候无法触发set方法,只有重新给变量重新赋值才会触发set。而且假如即使能触发set,我们在数组的更新的方法里也无法拿到同一个dep也就无法通知更新。

但是我们重写了数组方法,我们其实是能感知到数组调用了更新方法的,只不过获取不到对应的dep,所以现在我们要解决的问题是:需要对数组重新搞一个dep,这个dep要在读取的时候进行depend依赖收集,而且在调用数组更新的方法的地方能获取到同一个dep,从而进行notify。

这点我们在重写数组方法要对新加入的数据重新进行观测的时候就已经遇到个相似的问题,我们当时是在__ob__上获取Observer类来调用observeArray。

同样的思路,我们把dep放到,属性上的__ob__上就行了。

class Observer {
  constructor(data) {
      // 给对象和数组都加上dep
      this.dep = new Dep()
      Object.defineProperty(data, '__ob__', {
          value: this,
          enumerable: false
      })
      if(Array.isArray(data)) {
          data.__proto__ = newArrayProto
          this.observeArray(data)
      } else {
          this.walk(data)
      }
  }
  walk(data) { //循环对象 依次劫持
      Object.keys(data).forEach(key => defineReactive(data, key, data[key]))
  }
  observeArray(data) {
  // 遍历数组每一项直接执行observe,在observe中判断到是普通属性会直接return
      data.forEach(item => observe(item))
  }
}

解释下为啥数对象也要加这个dep(首先只有引用数据类型才会实例化Observer,observe方法里进行了判断),比如一个数据是a:{b:1} 我们直接给对象新增属性a.c=2众所周知是不会实现视图的更新的,需要用到$set,这个api是需要调用dep上的notify的。

数组和对象上的__ob__有了dep属性,那就set的时候进行依赖收集就好,这需要加一个判断,之前我们不是对数据进行了递归调用observe方法嘛,引用数据类型才会返回Observer类,基本数据类型会直接return,我们直接让一个变量等于这个这个方法,有值就证明是引用数据类型即__ob__上有dep,需要进行依赖收集

export function defineReactive(target, key, value) { 
  // 递归判断
  let childOb = observe(value) // 有值即是引用数据类型,返回值是实例化后的Observer
  let dep = new Dep()
  Object.defineProperty(target, key, {
      get() {
          // 可以进行调试
          if (Dep.target) {
            // 只有视图上使用的数据才进行依赖收集
            dep.depend();
            if (childOb) {
              childOb.dep.depend()
            }
          }
          return value
      },
      set(newValue) { 
          if(newValue === value) return 
          //设置的新值也要进行劫持
          observe(newValue)
          value = newValue 
          dep.notify()
      }
  })
}

调用数组更新方法的地方通过__ob__调用notify即可,我们之前已经留好口子了

methods.forEach(method => {
   ...
        if(inserted) {
            ob.observeArray(inserted)
        } 
        console.log('调用修改数组了');
        ob.dep.notify()
        return result
    }  
})

看下效果

image.png

image.png

看着没问题,要实现的效果都实现了。但是还有一种情况还没有考虑,数组嵌套数组。 如arr:[1,2,3,[4,5]],arr[3].push(6),此时就会发现内部数组缺少了依赖收集,所以需要对数组进行递归进行依赖收集。

function dependArray(value) {
  for (let i = 0; i < value.length; i++) {
      let current = value[i]
      current.__ob__ && current.__ob__.dep.depend()
      if(Array.isArray(current)) {
          dependArray(current)
      }
  }
}
...
if (childOb) {
    childOb.dep.depend();
    if (Array.isArray(value)) {
        dependArray(value);
    }
}

验证下效果叭

image.png

image.png

ok,大功告成。 下一篇写computed计算属性。