Vue源码学习3-响应式原理-defineReactive

  • 内容
  • 评论
  • 相关

前言

前面简单的梳理了 props data methods 的流程

从这篇文章开始,后面会深入理解Vue的核心之一:响应式原理,这篇文章可以稍微细看

--以下所有代码来自Vue 2.6.11 版本

Object.defineProperty

这个东西有什么用?我想只要是去了解过Vue响应式原理的人都应该知道,我就不多说了,不知道的就自行查阅。

可以在源码的这个目录 src/core/observer/index.js 中找到这段代码(代码略长,建议先跳过),defineReactive 在前面讲 props 的时候就见到过,作用就是将普通数据处理成响应式数据,然后就用到了 Object.defineProperty 这个方法。

export function defineReactive (
  obj: Object,
  key: string,
  val: any,
  customSetter?: ?Function,
  shallow?: boolean
) {
  const dep = new Dep()

  const property = Object.getOwnPropertyDescriptor(obj, key)
  if (property && property.configurable === false) {
    return
  }

  // cater for pre-defined getter/setters
  const getter = property && property.get
  const setter = property && property.set
  if ((!getter || setter) && arguments.length === 2) {
    val = obj[key]
  }

  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) {
      const value = getter ? getter.call(obj) : val
      if (newVal === value || (newVal !== newVal && value !== value)) {
        return
      }
      if (process.env.NODE_ENV !== 'production' && customSetter) {
        customSetter()
      }
      if (getter && !setter) return
      if (setter) {
        setter.call(obj, newVal)
      } else {
        val = newVal
      }
      childOb = !shallow && observe(newVal)
      dep.notify()
    }
  })
}

observe

observe 我们在 initData 方法的最后就有见到过,进到方法中可以看到一下代码

1:判断value是否是 Object 类型,observe只会对引用数据类型(Object或者Array)走后面的逻辑

2:判断value是否有 __ob__属性,如果有,说明它已经被Vue处理成了响应式对象了,无需走后面的逻辑了,这也是为了防止重复处理同一个对象

3:第二个 else if 判断略多,不过正常情况下我们是能够顺利走到 new Observer 逻辑的,Observe 的逻辑进去之后,value就会被打上 __ob__ 标记,即代表它已经是响应式对象了

4:Oberver是个class,定义在 src/core/observer/index.js 里面,它的整个逻辑也很简单(下面贴了段源码),构造函数中有个判断,数组和普通的Object对象各走一段逻辑,这里就看非数组情况下的逻辑(数组的逻辑后面单独拎出来写写就行了)

5:走到walk方法,就是一个遍历,类似于前面讲的 initProps 的逻辑,调用 defineReactive 方法处理对象中的每个字段

这个时候回看上面的 defineReactive 的代码,可以看到其中也调用了 observe 方法,走到这里就能梳理出一个递归处理 data 对象的逻辑

紧接着就是 Object.defineProperty 的逻辑,data或者props 对象中每个字段的值会通过闭包缓存在内存中,并且和一个Dep对象(它的作用后面来说)绑定在一起,当我们通过 this.xxx 获取 data 中的某个字段的时候,就会触发 get 的逻辑,修改则会触发 set 的逻辑 (this.xxx = xxxxx),get 和 set 就是Vue中所谓的依赖收集派发更新的源头

export function observe (value: any, asRootData: ?boolean): Observer | void {
  if (!isObject(value) || value instanceof VNode) {
    return
  }
  let ob: Observer | void
  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
}
export class Observer {
  value: any;bind
  dep: Dep;
  vmCount: number; // number of vms that have this object as root $data
  constructor (value: any) {
    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)
    }
  }
  walk (obj: Object) {
    const keys = Object.keys(obj)
    for (let i = 0; i < keys.length; i++) {
      defineReactive(obj, keys[i])
    }
  }
  .....代码省略
}

总结

代码走到这里,梳理出了一个递归处理 data 和 props 对象的逻辑,也知道了 defineReactive 方法的作用。

其中有两个class,一个Oberver 上面简单说了一下,还有一个 Dep 以及一个至此还未见到的重要的 class --Watcher,这两个东西是整个响应式原理的最重要的两个class,它们和两种设计模式密切相关 观察者模式订阅发布模式,这两个东西网上已经有很多人说过了,这里就不多扯了,不懂就自行查阅

 

评论

0条评论

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注