Skip to content
On this page

前文讲述了 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]
        }
    }
})

分析一下整体流程:

  1. 我们 new Observer() 的时候,会进去到 defineReactive 这个函数中,执行了 observe(val) 获取到了 Observer 实例对象,并给该对象设置了 getter 和 setter (注意:此时observe方法传入的val是数组arr

  2. 当调用该对象的 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 方法去更改数据

Released under the MIT License.