vue 响应式原理

响应式原理

简单点讲 vue 的响应式是通过 Object.defineProperty 和 观察者模式来实现的。
vue 初始化的时候 watcher 构造函数通过 Object.defineProperty 方法对 data 属性进行递归遍历,设置 get、set,初始化编译的时候会触发 getter 函数,进行依赖收集,将观察者 watcher 添加到目标对象 dep 中。改变数据的时候会触发 set, 执行 notify 方法,调用 dep 中 watcher 对象的 update 方法,update 方法将 watcher 添加到 watcher 队列中, 通过调用 nextTick 异步执行,触发更新。

至于实现的细节,现在细细道来...

我们需要分析的大致是以下源码文件:

  • observer
    • dep.js
    • index.js
    • scheduler.js
    • watcher.js

先看 index.js

export class Observer { // 定义 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
    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 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])
    }
  }
}

...
...
...

/**
 * Define a reactive property on an Object.
 */
export function defineReactive ( // 把对象的属性变成响应式 
  obj: Object,
  key: string,
  val: any,
  customSetter?: ?Function,
  shallow?: boolean
) {
  const dep = new Dep()

  const property = Object.getOwnPropertyDescriptor(obj, key) // 获取obj对象key属性的数据属性
  if (property && property.configurable === false) { // 如果该属性是不能修改或删除的,则直接返回
    return
  }

  // cater for pre-defined getter/setters
  const getter = property && property.get //尝试拿到该对象原生的get属性,保存到getter中
  const setter = property && property.set //尝试拿到该对象原生的set属性,保存到setter中
  if ((!getter || setter) && arguments.length === 2) { //如果getter不存在或者setter存在,且参数只有两个
    val = obj[key] //则直接通过obj[ke]获取值,并保存到val中
  }

  let childOb = !shallow && observe(val) //递归调用observe:当某个对象的属性还是对象时会进入
  Object.defineProperty(obj, key, { //调用Object.defineProperty设置obj对象的访问器属性
    enumerable: true,
    configurable: true,
    get: function reactiveGetter () {
      const value = getter ? getter.call(obj) : val
      if (Dep.target) {
        dep.depend() //调用depend()收集依赖, 
        if (childOb) { // 如果childOb存在
          childOb.dep.depend()  //则调用childOb.dep.depend()收集依赖
          if (Array.isArray(value)) { // 如果 val 是数组则调用小下面的函数处理
            dependArray(value)
          }
        }
      }
      return value
    },
    set: function reactiveSetter (newVal) {
      const value = getter ? getter.call(obj) : val //如果之前有定义getter,则调用getter获取值,否则就赋值为val
      /* eslint-disable no-self-compare */
      if (newVal === value || (newVal !== newVal && value !== 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) // newVal调用observe处理,newVal为数组或对象其属性也是响应式
      dep.notify() // 通知订阅的 watcher 做更新
    }
  })
}

接下来看 dep.js

 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 中存的 wather,循环执行 watcher 中的 update 方法
      subs[i].update()
    }
  }

接着看 watcher.js

/**
   * Subscriber interface.
   * Will be called when a dependency changes.
   */
  update () {
    /* istanbul ignore else */
    if (this.lazy) {
      this.dirty = true
    } else if (this.sync) {
      this.run()
    } else {
      queueWatcher(this) // 执行 watcher 队列 queueWatcher 方法
    }
  }

scheduler.js:

export function queueWatcher (watcher: Watcher) {
  const id = watcher.id
  if (has[id] == null) { // 相同的 watcher 只添加进队列一次
    has[id] = true
    if (!flushing) {
      queue.push(watcher)
    } else {
      // if already flushing, splice the watcher based on its id
      // if already past its id, it will be run next immediately.
      let i = queue.length - 1
      while (i > index && queue[i].id > watcher.id) {
        i--
      }
      queue.splice(i + 1, 0, watcher)
    }
    // queue the flush
    if (!waiting) {
      waiting = true

      if (process.env.NODE_ENV !== 'production' && !config.async) {
        flushSchedulerQueue()
        return
      }
      nextTick(flushSchedulerQueue)  // 调用异步 nestTick 处理 watcher 队列
    }
  }
}
原文地址:https://www.cnblogs.com/hi-shepherd/p/12608651.html