上一章,咱们讲到了Vue初始化做的一些操作,那么咱们这一章来讲一个Vue外围概念响应式零碎
咱们先来看一下官网对深刻响应式零碎的解释:

当你把一个一般的 JavaScript 对象传给 Vue 实例的 data 选项,Vue 将遍历此对象所有的属性。并应用 Object.defineProperty 把这些属性全副转为 getter/setter。Object.defineProperty 是 ES5 中一个无奈 shim 的个性。这也就是为什么 Vue 不反对 IE8 以及更低版本浏览器的起因。

上图是Vue官网放出的一张图,而且提到外围概念Object.defineProperty,那么咱们间接看源码,咱们看到的Object.definePropertydefineReactive函数的外部,而defineReactive函数在walk函数外部,顺次找到源头是Observer

./core/observer/indexexport class Observer {  value: any;  dep: Dep;  vmCount: number; // number of vms that has this object as root $data  /**   * 生成的Observer实例上挂载三个属性   * 1. value, 即观测数据对象自身   * 2. dep, 用于依赖收集的容器   * 3. vmCount, 间接写死为0   */  constructor (value: any) {    this.value = value    this.dep = new Dep()    this.vmCount = 0    // 在观测数据对象上增加__ob__属性, 是Observer实例的援用    // def相当于Object.defineProperty, 区别是dep里会把__ob__属性设置为不可枚举    // 须要留神的是, value.__ob__.value 显然就是 value 自身, 这里有一个循环援用    def(value, '__ob__', this)    if (Array.isArray(value)) {      const augment = hasProto        ? protoAugment        : copyAugment      augment(value, arrayMethods, arrayKeys)      this.observeArray(value)    } else {      this.walk(value)    }  }  // 用于解决对象类型的观测值, 循环所有的key都调用一次defineReactive  walk (obj: Object) {    const keys = Object.keys(obj)    for (let i = 0; i < keys.length; i++) {      defineReactive(obj, keys[i])    }  }  // 对数组的每一项进行监听  observeArray (items: Array<any>) {    for (let i = 0, l = items.length; i < l; i++) {      observe(items[i])    }  }}

value是须要被察看的数据对象,在构造函数中,会给value减少ob属性,作为数据曾经被Observer察看的标记。如果value数组,就应用observeArray遍历value,对value中每一个元素调用observe别离进行察看。如果value对象,则应用walk遍历value上每个key,对每个key调用defineReactive来取得该keyset/get控制权。

那么说到如果value是数组的话,调用observeArray办法遍历数组,开端还调用了observe函数,那到底这个函数有什么用呢?咱们来一探到底:

// 用于观测一个数据export function observe (value: any, asRootData: ?boolean): Observer | void {  // 对于不是object或者是vnode实例的数据, 间接返回, 不会进行观测  if (!isObject(value) || value instanceof VNode) {    return  }  let ob: Observer | void  // 如果数据上已有__ob__属性, 阐明该数据曾经被观测, 不再反复解决  if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) {    ob = value.__ob__  // 要观测一个数据须要满足以下条件:   // 1. shouldObserve为true, 这是一个标记位, 默认为true, 某些非凡状况下会改成false  // 2. !isServerRendering(), 不能是服务端渲染  // 3. Array.isArray(value) || isPlainObject(value), 要观测的数据必须是数组或者对象  // 4. Object.isExtensible(value). 要观测的数据必须是可扩大的  // 5. !value._isVue, 所有vue实例的_isVue属性都为true, 防止观测vue实例对象  } 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}

能够见得observe函数的作用是:查看对象上是否有ob属性,如果存在,则表明该对象曾经处于Observer的察看中,如果不存在,则new Observer来察看对象。
回到上文,数组说完了,那么来说对象的函数walk调用,咱们看到间接是调用了defineReactive函数,那咱们来一探到底:

// 定义响应式对象, 给对象动静增加get set拦挡办法,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  if (!getter && arguments.length === 2) {    val = obj[key]  }  const setter = property && property.set  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      /* 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()      }      if (setter) {        setter.call(obj, newVal)      } else {        val = newVal      }      childOb = !shallow && observe(newVal)      dep.notify()    }  })}

能够见得defineReactive函数的作用是:通过Object.defineProperty设置对象的key属性,使得可能捕捉到该属性值的set/get操作,且observe函数深度遍历,所以把所有的属性都增加到了Observe下面了,也就是说,咱们对数据的读写就会触发getter/setter,再者咱们能够看到get办法外面有Dep.target这个变量,dep.dependdependArrayset办法外面有dep.notify这些办法,可想而知,咱们依赖了Dep这个文件:

export default class Dep {  // target是一个全局惟一的Watcher  static target: ?Watcher;  id: number;  subs: Array<Watcher>;  // 生成每个实例惟一的uid, subs用于存储watcher  constructor () {    this.id = uid++    this.subs = []  }  // 增加一个watcher  addSub (sub: Watcher) {    this.subs.push(sub)  }  // 删除一个watcher  removeSub (sub: Watcher) {    remove(this.subs, sub)  }  // 将本身退出到全局的watcher中  depend () {    if (Dep.target) {      Dep.target.addDep(this)    }  }  // 告诉所有订阅者  notify () {    // stabilize the subscriber list first    const subs = this.subs.slice()    for (let i = 0, l = subs.length; i < l; i++) {      subs[i].update()    }  }}

察看dep文件,咱们能够看到一个Dep类,其中有几个办法:

  • addSub: 接管的参数为Watcher实例,并把Watcher实例存入记录依赖的数组中
  • removeSub:addSub对应,作用是将Watcher实例从记录依赖的数组中移除
  • depend: Dep.target上寄存这以后须要操作的Watcher实例,调用depend会调用该 Watcher实例的addDep办法。
  • notify: 告诉依赖数组中所有的watcher进行更新操作
    而且发明了一个subs用来存储订阅者。
    剖析完了之后,咱们就总结出一句话,dep是一个用来存储所有订阅者watcher的对象,他的notify办法就是去遍历告诉所有的Watcher订阅者数据源产生了扭转须要去更新视图了。
    那么咱们再来看一下Watcher的构造是咋样的:
  • 参考Vue3源码视频解说:进入学习
export default class Watcher {  vm: Component;  expression: string;  cb: Function;  id: number;  deep: boolean;  user: boolean;  lazy: boolean;  sync: boolean;  dirty: boolean;  active: boolean;  deps: Array<Dep>;  newDeps: Array<Dep>;  depIds: SimpleSet;  newDepIds: SimpleSet;  getter: Function;  value: any;  constructor (    vm: Component,    expOrFn: string | Function,    cb: Function,    options?: ?Object,    isRenderWatcher?: boolean  ) {    this.vm = vm    if (isRenderWatcher) {      vm._watcher = this    }    vm._watchers.push(this)    // options    if (options) {      this.deep = !!options.deep      this.user = !!options.user      this.lazy = !!options.lazy      this.sync = !!options.sync    } else {      this.deep = this.user = this.lazy = this.sync = false    }    this.cb = cb    this.id = ++uid // uid for batching    this.active = true    this.dirty = this.lazy // for lazy watchers    this.deps = []    this.newDeps = []    this.depIds = new Set()    this.newDepIds = new Set()    this.expression = process.env.NODE_ENV !== 'production'      ? expOrFn.toString()      : ''    // parse expression for getter    if (typeof expOrFn === 'function') {      this.getter = expOrFn    } else {      this.getter = parsePath(expOrFn)      if (!this.getter) {        this.getter = function () {}        process.env.NODE_ENV !== 'production' && warn(          `Failed watching path: "${expOrFn}" ` +          'Watcher only accepts simple dot-delimited paths. ' +          'For full control, use a function instead.',          vm        )      }    }    this.value = this.lazy      ? undefined      : this.get()  }  /**   * Evaluate the getter, and re-collect dependencies.   */  get () {    pushTarget(this)    let value    const vm = this.vm    try {      value = this.getter.call(vm, vm)    } catch (e) {      if (this.user) {        handleError(e, vm, `getter for watcher "${this.expression}"`)      } else {        throw e      }    } finally {      // "touch" every property so they are all tracked as      // dependencies for deep watching      if (this.deep) {        traverse(value)      }      popTarget()      this.cleanupDeps()    }    return value  }  /**   * 接管参数`dep(Dep实例)`,让以后`watcher`订阅`dep`   */  addDep (dep: Dep) {    const id = dep.id    if (!this.newDepIds.has(id)) {      this.newDepIds.add(id)      this.newDeps.push(dep)      if (!this.depIds.has(id)) {        dep.addSub(this)      }    }  }  /**   * 革除`newDepIds和newDep`上记录的对dep的订阅信息   */  cleanupDeps () {    let i = this.deps.length    while (i--) {      const dep = this.deps[i]      if (!this.newDepIds.has(dep.id)) {        dep.removeSub(this)      }    }    let tmp = this.depIds    this.depIds = this.newDepIds    this.newDepIds = tmp    this.newDepIds.clear()    tmp = this.deps    this.deps = this.newDeps    this.newDeps = tmp    this.newDeps.length = 0  }  /**   * 立即运行`watcher`或者将`watcher`退出队列中期待对立`flush`   */  update () {    /* istanbul ignore else */    if (this.lazy) {      this.dirty = true    } else if (this.sync) {      this.run()    } else {      queueWatcher(this)    }  }  /**   * 运行`watcher`,调用`this.get()`求值,而后触发回调   */  run () {    if (this.active) {      const value = this.get()      if (        value !== this.value ||        // Deep watchers and watchers on Object/Arrays should fire even        // when the value is the same, because the value may        // have mutated.        isObject(value) ||        this.deep      ) {        // set new value        const oldValue = this.value        this.value = value        if (this.user) {          try {            this.cb.call(this.vm, value, oldValue)          } catch (e) {            handleError(e, this.vm, `callback for watcher "${this.expression}"`)          }        } else {          this.cb.call(this.vm, value, oldValue)        }      }    }  }  /**   *调用`this.get()`求值   */  evaluate () {    this.value = this.get()    this.dirty = false  }  /**   * 遍历`this.deps`,让以后`watcher`实例订阅所有`dep`   */  depend () {    let i = this.deps.length    while (i--) {      this.deps[i].depend()    }  }  /**   *去除以后`watcher`实例所有的订阅   */  teardown () {    if (this.active) {      // remove self from vm's watcher list      // this is a somewhat expensive operation so we skip it      // if the vm is being destroyed.      if (!this.vm._isBeingDestroyed) {        remove(this.vm._watchers, this)      }      let i = this.deps.length      while (i--) {        this.deps[i].removeSub(this)      }      this.active = false    }  }}

咱们看到了一个Watcher类,并且有一些办法:

  • get:Dep.target设置为以后watcher实例,在外部调用this.getter,如果此时某个被 Observer察看的数据对象被取值了,那么以后watcher实例将会主动订阅数据对象的Dep实例
  • addDep: 接管参数dep(Dep实例),让以后watcher订阅dep
  • cleanupDeps: 革除newDepIds和newDep上记录的对dep的订阅信息
  • update: 立即运行watcher或者将watcher退出队列中期待对立fresh
  • run: 运行watcher,调用this.get()求值,而后触发回调
  • evaluate: 调用this.get()求值
  • depend: 遍历this.deps,让以后watcher实例订阅所有dep
  • teardown: 去除以后watcher实例所有的订阅
    那么咱们晓得这么多办法,来梳理一下流程

咱们的数据发生变化,咱们data外面所有的属性都能够看做一个dep,而dep外面的subs就是寄存以后属性的中央,当咱们数据发生变化的时候就不会被监听到,咱们就要通过dep去调用notify办法告诉所有的Watcher进行更新视图。
那么问题又来了,这个this.subs是如何增加订阅者的?

get () {    pushTarget(this)    let value    const vm = this.vm    try {      value = this.getter.call(vm, vm)    } catch (e) {      if (this.user) {        handleError(e, vm, `getter for watcher "${this.expression}"`)      } else {        throw e      }    } finally {      // "touch" every property so they are all tracked as      // dependencies for deep watching      if (this.deep) {        traverse(value)      }      popTarget()      this.cleanupDeps()    }    return value  }  /**   * Add a dependency to this directive.   */  addDep (dep: Dep) {    const id = dep.id    if (!this.newDepIds.has(id)) {      this.newDepIds.add(id)      this.newDeps.push(dep)      if (!this.depIds.has(id)) {        dep.addSub(this)      }    }  }

咱们在Dep中能够看到Dep在一开始定义了一个全局属性Dep.target,在新建watcher是,这个属性为null,而在watcher的构造函数中最初会执行本人的get()办法,进而执行pushTarget(this)办法:

// 将watcher实例赋值给Dep.target,用于依赖收集。同时将该实例存入target栈中export function pushTarget (_target: ?Watcher) {  if (Dep.target) targetStack.push(Dep.target)  Dep.target = _target}

能够看到get()办法,value = this.getter.call(vm, vm),而后popTarget()办法:

// 从target栈取出一个watcher实例export function popTarget () {  Dep.target = targetStack.pop()}

Dep.target只是一个标记,存储以后的watcher实例,触发Object.defineProperty中的get拦挡,而在Oject.defineProperty中的get那里,咱们能够看到dep.depend(),正是在这里将以后的订阅者watcher绑定当Dep上。
也就是说,每个watcher第一次实例化的时候,都会作为订阅者订阅其相应的Dep

写到这里,置信各位对数据响应式曾经有很粗浅的了解了吧,那么咱们还有一个话题,咱们是如何进行初始化渲染更新二次更新视图的?下章咱们讨论一下。