Observer源码解析

1,059 阅读4分钟

前言

在Observer源码部分主要有三个角色:Observer、Dep、Watcher。

Observer

看这一部分,可以带着以下几个问题来看:

1、满足什么条件可以将其变成响应式的

2、Observer是如何去分别处理传入的数组或者对象的?

3、有两处new Dep,作用分别是什么?

4、核心代码defineReactive干了些什么?

5、Dep.target是什么?defineReactive中get时,为什么要判断Dep.target?

满足什么条件可以将其变成响应式的

function observe (value, asRootData) {
  if (!isObject(value) || value instanceof VNode) {
    return
  }
  let ob
  if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) {
    ob = value.__ob__
  } else if (
    shouldObserve &&
    !isServerRendering() &&
    (Array.isArray(value) || isPlainObject(value)) &&
    Object.isExtensible(value) &&
    !value._isVue
  ) {
    ob = new Observer(value)
  }
  if (asRootData && ob) {
    ob.vmCount++
  }
  return ob
}

其实上述代码中有很多判断,我们可以得出以下结论:

(1)必须是一个对象,且不能是vnode的类型的。

Observer是如何去分别处理传入的数组或者对象的

if (Array.isArray(value)) {
  if (hasProto) {
    protoAugment(value, arrayMethods)
  } else {
    copyAugment(value, arrayMethods, arrayKeys)
  }
  this.observeArray(value)
} else {
  this.walk(value)
}

这段代码就是判断传入的值是不是数组,如果是数组,走observeArray方法,如果不是数组,那么走walk方法。

walk (obj) {
    const keys = Object.keys(obj)
    for (let i = 0; i < keys.length; i++) {
      defineReactive(obj, keys[i])
    }
}
observeArray (items) {
    for (let i = 0, l = items.length; i < l; i++) {
      observe(items[i])
    }
}

walk这个方法就是遍历对象,然后给对象中的属性值变成响应式的,遍历完之后,整个对象就是响应式对象了。 observeArray这个方法是遍历数组,然后对数组中每一个元素在走一遍响应式流程。

有两处new Dep,作用分别是什么

第一处:

class Observer {
  constructor (value) {
    this.value = value
    this.dep = new Dep()
    this.vmCount = 0
    def(value, '__ob__', this)
    if (Array.isArray(value)) {
      if (hasProto) {
        protoAugment(value, arrayMethods)
      } else {
        copyAugment(value, arrayMethods, arrayKeys)
      }
      this.observeArray(value)
    } else {
      this.walk(value)
    }
  }

第二处:

function defineReactive (
  obj,
  key,
  val,
  customSetter,
  shallow
) {
  const dep = new Dep()
  // 省略中间代码
  Object.defineProperty(obj, key, {
    enumerable: true,
    configurable: true,
    get: function reactiveGetter () {
      const value = getter ? getter.call(obj) : val
      if (Dep.target) {
        dep.depend()
        // 省略中间代码
      }
      return value
    },
    set: function reactiveSetter (newVal) {
      // 省略中间代码
      dep.notify()
    }
  })
}

第二处dep我们很好理解,是给get和set服务的,进行依赖收集和派发更新。

第一处dep,我们可以考虑一下,他是整个对象的一个属性,那么他何时进行依赖收集和派发更新。

我们可以全局搜一下dep.depend。发现会有三处。有两处是对ob属性进行操作的,也就是对整个对象进行依赖收集。

在全局搜一下dep.notify。发现有四处。有三处是对ob属性进行操作的。分别是set和del,数组的一个方法。

核心代码defineReactive干了些什么

这一部分代码可以分为三部分看:定义一些变量、Object.defineProperty、对childOb操作。

  // 生成一个新的dep。
  const dep = new Dep()
  // 判断这个对象这个属性是否可以修改
  const property = Object.getOwnPropertyDescriptor(obj, key)
  if (property && property.configurable === false) {
    return
  }
  // 定义getter和setter方法
  const getter = property && property.get
  const setter = property && property.set
  if ((!getter || setter) && arguments.length === 2) {
    val = obj[key]
  }
get: function reactiveGetter () {
  // 获取值
  const value = getter ? getter.call(obj) : val
  // 当存在Dep.target的时候进行依赖收集
  if (Dep.target) {
    dep.depend()
    // 省略中间代码
  }
  // 返回获取到的值
  return value
},
set: function reactiveSetter (newVal) {
  // 获取原来的值
  const value = getter ? getter.call(obj) : val
  // 将新的值和原值进行对比,如果没有发生改变,就直接返回
  if (newVal === value || (newVal !== newVal && value !== value)) {
    return
  }
  // 该属性不能set的情况也直接返回
  if (getter && !setter) return
  // 给该属性赋值
  if (setter) {
    setter.call(obj, newVal)
  } else {
    val = newVal
  }
  // 重新对这个值进行监听
  childOb = !shallow && observe(newVal)
  // 更新dep中的watcher
  dep.notify()
}
// 尝试将值转化成响应式对象
let childOb = !shallow && observe(val)
  Object.defineProperty(obj, key, {
    enumerable: true,
    configurable: true,
    get: function reactiveGetter () {
      const value = getter ? getter.call(obj) : val
      if (Dep.target) {
        dep.depend()
        // 如果值能呗转化成响应式对象,那么对整个对象进行依赖收集
        if (childOb) {
          childOb.dep.depend()
          if (Array.isArray(value)) {
            dependArray(value)
          }
        }
      }
      return value
    },
    set: function reactiveSetter (newVal) {
      // 省略中间代码
      //因为值发生了变动,所以再一次尝试将其变成一个响应式对象
      childOb = !shallow && observe(newVal)
    }
  })

Dep.target是什么

其实Dep.target是一个全局变量,更是一个wathcer。在dep文件中的源码如下:

Dep.target = null
const targetStack = []

export function pushTarget (target: ?Watcher) {
  targetStack.push(target)
  Dep.target = target
}

export function popTarget () {
  targetStack.pop()
  Dep.target = targetStack[targetStack.length - 1]
}

defineReactive中get时,为什么要判断Dep.target

因为所有定义在data中的值,都会被变成响应式对象,但是每一个不一定有watcher。watcher分为三种:render中生成的watcher、用户自定义的watcher、computed。

上述流程中,当生成一个新的renderWatcher的时候,便会走get流程,然后进行依赖收集,如果没有Dep.target,说明这个值并没有对应的watcher,所以不需要进行依赖收集。 当更新的是时候,又回进行一次依赖收集。