Observer源码解析

前言

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

Observer

看这一部分,能够带着如下几个问题来看:react

一、知足什么条件能够将其变成响应式的数组

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

三、有两处new Dep,做用分别是什么?this

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

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

知足什么条件能够将其变成响应式的

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
}
复制代码

其实上述代码中有不少判断,咱们能够得出如下结论:cdn

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

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,因此不须要进行依赖收集。 当更新的是时候,又回进行一次依赖收集。

相关文章
相关标签/搜索