Vue源码阅读笔记—— Observer

196 阅读2分钟

前言

所看的源码版本是:"version": "2.6.14"。

源码笔记

一、初始化数据

vue的入口src/core/instance/index.js,里面做了一系列的初始化:

image.png 然后我们看到initMixin中的initState(vm)

export function initState (vm: Component) {
  ...
  // 我们主要看处理data部分代码
  if (opts.data) {
    initData(vm)
  } else {
    observe(vm._data = {}, true /* asRootData */)
  }
  ...
}
function initData (vm: Component) {
  // 下面接着主要就是拿到data,然后通过observe方法去处理
  let data = vm.$options.data
  data = vm._data = typeof data === 'function'
    ? getData(data, vm)
    : data || {}
  ...省略代码
  // observe data
  observe(data, true /* asRootData */)
}

二、observe

observe 代码入口位置 src/core/observer/index.js

export function observe (value: any, asRootData: ?boolean): Observer | void {
  // 里面主要做了一些判断啥的,我们主要看 new Observer(value) 部分
  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
}

里面主要有个new Observer(value),我们接着看这个类:

export class Observer {
  value: any;
  dep: Dep;
  vmCount: number; // number of vms that have this object as root $data

  constructor (value: any) {
    this.value = value
    this.dep = new Dep() // Dep类我们先不看
    this.vmCount = 0
    def(value, '__ob__', this) // 这里通过def给每个都添加__ob__得属性
    if (Array.isArray(value)) { // 这里如果是数组做对应得数组处理
      if (hasProto) {
        protoAugment(value, arrayMethods)
      } else {
        copyAugment(value, arrayMethods, arrayKeys)
      }
      this.observeArray(value)
    } else {
      this.walk(value) // 处理对象
    }
  }

  /**
   * Walk through all properties and convert them into
   * getter/setters. This method should only be called when
   * value type is Object.
   */
  walk (obj: Object) {
    // 循环遍历对象 给每个属性 添加响应式
    const keys = Object.keys(obj)
    for (let i = 0; i < keys.length; i++) {
      defineReactive(obj, keys[i])
    }
  }

  /**
   * Observe a list of Array items.
   */
  observeArray (items: Array<any>) {
    for (let i = 0, l = items.length; i < l; i++) {
      observe(items[i]) // 对于数组每项进行观察
    }
  }
}

它里面def方法比较简单好理解(src/core/util/lang.js)

export function def (obj: Object, key: string, val: any, enumerable?: boolean) {
  Object.defineProperty(obj, key, {
    value: val,
    enumerable: !!enumerable,
    writable: true,
    configurable: true
  })
}

然后就是核心得defineReactive,可以对数据进行劫持。

三、defineReactive

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)
  
  // vue2 数据响应式就是通过Object.defineProperty实现的相信大家都知道
  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
      /* eslint-disable no-self-compare */
      if (newVal === value || (newVal !== newVal && value !== value)) {
        return
      }
      /* eslint-enable no-self-compare */
      if (process.env.NODE_ENV !== 'production' && customSetter) {
        customSetter()
      }
      // #7981: for accessor properties without setter
      if (getter && !setter) return
      if (setter) {
        setter.call(obj, newVal)
      } else {
        val = newVal
      }
      childOb = !shallow && observe(newVal) // 对于新修改的值再去进行观察
      dep.notify()
    }
  })
}

defineReactive里面主要就是递归去给对象属性一个个添加响应式,然后通过Object.definePropertygetset方法实现数据劫持。

总结

看完Observer其实就是对传入的data通过递归循环添加数据响应式,然后通过Object.defineProperty去数据进行劫持;其实里面主要针对数组和对象不同的处理,我们先说了对象,后续在阅读对数组的观察、劫持方式。