细读Vue2.6.14 Core 源码(6): defineReactive

细读Vue2.6.14 Core 源码(6): defineReactive

    /**
        @date 2021-09-12
        @description 细读Vue2.6.14 Core 源码(6): defineReactive
    */
复制代码

壹(序)

这一章着重于 Vue 的响应式,通过调用 defineReactive 实现。

贰(defineReactive)

export function defineReactive (
  obj: Object,
  key: string,
  val: any,
  customSetter?: ?Function,
  shallow?: boolean
) {
  // 声明 dep,每个声明为响应式的值都有一个自己的 dep
  const dep = new Dep()
  // 获取属性描述符
  const property = Object.getOwnPropertyDescriptor(obj, key)
  // configurable 为 false 则 return
  if (property && property.configurable === false) {
    return
  }

  // cater for pre-defined getter/setters
  // 获取 getter 和 setter
  const getter = property && property.get
  const setter = property && property.set
  // 此情况下赋值 val
  if ((!getter || setter) && arguments.length === 2) {
    val = obj[key]
  }
  // 观察者
  // 对于非 object 的 value,只是简单的在 getter 中收集依赖
  // 对于 object 会进行观察,产生一个观察者,在观察者中再调用 defineReactive 进行响应式处理,相当于递归处理
  // 如果观察者中调用 defineReactive 时,又是一个 object,那么会再进行观察
  // 此时就形成 defineReactive => observe => new Observer() => observer.walk() => defineReactive => observe...
  // 通过此方式实现 object 的深层次响应式处理
  // 对于数组则是进行判断,再遍历数组进行观察处理,又回到此处
  let childOb = !shallow && observe(val)
  // 使用 Object.defineProperty 设置 getter 和 setter
  Object.defineProperty(obj, key, {
    enumerable: true,
    configurable: true,
    // getter
    get: function reactiveGetter () {
      // 获取 value
      const value = getter ? getter.call(obj) : val
      // 在 pushTarget 函数中会赋值 Dep.target(初始值为 null,赋值为一个 watcher)
      // 可以使用 popTarget 函数移除当前 Dep.target
      // 保证只同时处理一个 watcher
      // 在声明组件的 watcher 时会调用 watcher.get,将当前 watcher push进 targetStack,且 Dep.target = watcher
      if (Dep.target) {
        // 收集依赖
        dep.depend()
        if (childOb) {
          // object 收集依赖
          childOb.dep.depend()
          if (Array.isArray(value)) {
            // 数组收集依赖
            dependArray(value)
          }
        }
      }
      return value
    },
    set: function reactiveSetter (newVal) {
      // 获取当前 value
      const value = getter ? getter.call(obj) : val
      /* eslint-disable no-self-compare */
      // 值并未变化,或都为 NaN
      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
      }
      // 重新观察 newVal
      childOb = !shallow && observe(newVal)
      // 通知所有依赖更新
      dep.notify()
    }
  })
}

复制代码

叁(observe)

export function observe (value: any, asRootData: ?boolean): Observer | void {
  // 不是 object 或 是VNode则 return
  if (!isObject(value) || value instanceof VNode) {
    return
  }
  let ob: Observer | void
  // 已经被观察过了,直接使用 __b__
  if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) {
    ob = value.__ob__
  } else if (
    shouldObserve && // 需要被观察
    !isServerRendering() && // 不是服务端渲染
    (Array.isArray(value) || isPlainObject(value)) && // value 是 array 或 纯object
    Object.isExtensible(value) && // 当前 value 是否可扩展
    !value._isVue // 不是 Vue 实例
  ) {
    // 创建新的观察者
    ob = new Observer(value)
  }
  if (asRootData && ob) {
    ob.vmCount++
  }
  return ob
}
复制代码

肆(Observer)

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()
    this.vmCount = 0
    // 定义 __ob__ 
    def(value, '__ob__', this)
    // value 为数组
    if (Array.isArray(value)) {
      // 当前环境中对象有 __proto__ 属性
      if (hasProto) {
        protoAugment(value, arrayMethods)
      } else {
        copyAugment(value, arrayMethods, arrayKeys)
      }
      // 观察此数组,遍历调用 observe 进行递归
      this.observeArray(value)
    } else {
      // 调用 walk 函数
      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])
    }
  }
}
复制代码

伍(Dep)

export default class Dep {
  static target: ?Watcher;
  id: number;
  subs: Array<Watcher>;

  constructor () {
    this.id = uid++
    this.subs = []
  }

  addSub (sub: Watcher) {
    this.subs.push(sub)
  }

  removeSub (sub: Watcher) {
    remove(this.subs, sub)
  }

  depend () {
    if (Dep.target) {
      Dep.target.addDep(this)
    }
  }

  notify () {
    // stabilize the subscriber list first
    const subs = this.subs.slice()
    if (process.env.NODE_ENV !== 'production' && !config.async) {
      // subs aren't sorted in scheduler if not running async
      // we need to sort them now to make sure they fire in correct
      // order
      subs.sort((a, b) => a.id - b.id)
    }
    // 更新
    for (let i = 0, l = subs.length; i < l; i++) {
      subs[i].update()
    }
  }
}
复制代码

终(导航)

细读Vue2.6.14 Core 源码(1): 入口

细读Vue2.6.14 Core 源码(2): After Vue

细读Vue2.6.14 Core 源码(3): initGlobalAPI

细读Vue2.6.14 Core 源码(4): _init

细读Vue2.6.14 Core 源码(5): initState

细读Vue2.6.14 Core 源码(6): defineReactive

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享