Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Vue源码-双向数据绑定原理(二) #5

Open
7kyun opened this issue May 11, 2022 · 0 comments
Open

Vue源码-双向数据绑定原理(二) #5

7kyun opened this issue May 11, 2022 · 0 comments

Comments

@7kyun
Copy link
Owner

7kyun commented May 11, 2022

defineReactive

defineReactive 就是定义对象上的响应性属性的方法
它是通过 Object.defineProperty 为数据定义上 getter\setter 方法
该方法会形成一个闭包,实例化一个私有的 Dep 实例进行该对象的依赖收集,改变数据触发 setter 的时候会由 Dep 通知所有的 Watcher 观察者对象进行视图的更新。

/**
 * Define a reactive property on an Object.
 * 定义对象上的响应性属性。
 */
export function defineReactive (
  obj: Object,
  key: string,
  val: any,
  customSetter?: ?Function,
  shallow?: boolean
) {
  // 实例化一个私有的 dep 提供给后面新定义的 getter/setter 调用
  const dep = new Dep()

  // 获取对象上对应属性的描述符
  const property = Object.getOwnPropertyDescriptor(obj, key)

  // 描述符可更改
  if (property && property.configurable === false) {
    return
  }

  // cater for pre-defined getter/setters
  // 如果已定义过 getter/setter 则先将其缓存下来 在后续覆盖定义之后再执行
  const getter = property && property.get
  const setter = property && property.set
  if ((!getter || setter) && arguments.length === 2) {
    val = obj[key]
  }

  // 将子对象再进行 observe 监听 并缓存返回的实例
  let childOb = !shallow && observe(val)

  // 进行响应式定义
  Object.defineProperty(obj, key, {
    enumerable: true,
    configurable: true,
    get: function reactiveGetter () {
      // 先执行原有的 getter 方法
      const value = getter ? getter.call(obj) : val
      if (Dep.target) {
        // 依赖收集
        dep.depend()
        if (childOb) {
          // 子对象进行依赖收集
          // 其实就是将同一个watcher观察者实例放进了两个depend中
          // 一个是正在本身闭包中的depend,另一个是子元素的depend
          childOb.dep.depend()
          if (Array.isArray(value)) {
            dependArray(value)
          }
        }
      }
      return value
    },
    set: function reactiveSetter (newVal) {
      // 通过 getter 方法获取旧值
      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
      // 对于没有setter的访问器属性
      if (getter && !setter) return

      if (setter) {
        // 先执行原有的 setter 方法
        setter.call(obj, newVal)
      } else {
        val = newVal
      }

      // 对新值再进行监听
      childOb = !shallow && observe(newVal)
      // 由 dep 通知所有 watcher
      dep.notify()
    }
  })
}

getter 进行依赖收集的过程中,对于数组元素还会进行深度递归的依赖收集

/**
 * Collect dependencies on array elements when the array is touched, since
 * we cannot intercept array element access like property getters.
 */
function dependArray (value: Array<any>) {
  for (let e, i = 0, l = value.length; i < l; i++) {
    e = value[i]
    // 进行依赖收集
    e && e.__ob__ && e.__ob__.dep.depend()
    // 若值为数组 则 继续递归收集
    if (Array.isArray(e)) {
      dependArray(e)
    }
  }
}

observe方法

这个方法会返回一个Observer实例,同时将视力绑定到响应式数据的__ob__属性上,并不会重复绑定
相关源码

/**
 * Attempt to create an observer instance for a value,
 * returns the new observer if successfully observed,
 * or the existing observer if the value already has one.
 */
/**
 * 尝试为一个值创建一个 Observer 实例(__ob__),
 * 如果值已经有了 Observer 实例,则返回现有 Observer 实例。
 * 如果没有且成功监听,则返回新的 Observer,
 */
export function observe (value: any, asRootData: ?boolean): Observer | void {
  // 判断 非对象|vnode 直接返回
  if (!isObject(value) || value instanceof VNode) {
    return
  }
  let ob: Observer | void
  // 判断是否存在 Observer 实例
  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
}

下一篇解读 Observer 类和数组的响应式实现

@7kyun 7kyun changed the title Vue 响应式原理(三) Vue源码-双向数据绑定原理(三) Jul 14, 2022
@7kyun 7kyun changed the title Vue源码-双向数据绑定原理(三) Vue源码-双向数据绑定原理(二) Jul 14, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant