Skip to content

Latest commit

 

History

History
150 lines (126 loc) · 4.25 KB

3.md

File metadata and controls

150 lines (126 loc) · 4.25 KB

入口开始,解读Vue源码(三)—— initMixin 上篇

这边文章我们主要去解读 initMixin后续的执行过程,上篇我们已经可以看到,接下来主要会发生这几个操作过程:

    initLifecycle(vm)
    initEvents(vm)
    initRender(vm)
    callHook(vm, 'beforeCreate')
    initInjections(vm)
    initState(vm)
    initProvide(vm)
    callHook(vm, 'created')

在我们一个个了解之前,首选我们需要了解一下响应式数据原理,也就是我们常说的:订阅-发布 模式。

先从 Observer 开始谈起

Observer

export class Observer {
  value: any;
  dep: Dep;
  vmCount: number; // number of vms that has 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)) {
      const augment = hasProto
        ? protoAugment
        : copyAugment
      augment(value, arrayMethods, arrayKeys)
      this.observeArray(value)
    } else {
      this.walk(value)
    }
  }

  /**
   * Walk through each property 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], 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])
    }
  }
}

当我们调用new Observer(value)的时候,会去执行this.walk(value)这个方法,其功能主要是遍历value的属性,通过Object.defineProperty方法来定义响应式数据。那么是如何实现响应通知的呢?来看一下他的具体代码:

defineReactive

export function defineReactive (
 obj: Object,
 key: string,
 val: any,
 customSetter?: ?Function,
 shallow?: boolean
) {
  const dep = new Dep()
  ...
  Object.defineProperty(obj, key, {
      ...
      get: function reactiveGetter () {
        ...
        dep.depend()
        ...
        return value
      },
      set: function reactiveSetter (newVal) {
        ...
        dep.notify()
      }
    })
}

这里我们省略了部分代码,主要注意Dep 这个东西,也就是我们常说的订阅器,这里主要做了2件事情:

  1. dep.depend()
  2. dep.notify()

Dep

通过上面的图,我们知道,Dom 上通过指令或者双大括号绑定的数据,会为数据进行添加观察者watcher,当实例化Watcher的时候 会触发属性的getter方法,此时会调用dep.depend()。我们看一下depend方法:

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

Dep.target 是什么东西呢?其实在进行Watcher实例化的时候,会调用内部的get函数,开始为他初始化

get () {
  pushTarget(this)
  ...
}

Watcher

其中pushTarget 方法就是为Dep.target绑定此watcher实例,所以Dep.target.addDep(this)也就是执行此实例中的addDep方法

addDep (dep: Dep) {
  ...
  dep.addSub(this)
}

这样便为我们的dep实例添加了一个watcher实例。

接着当我们更新data的时候,会触发他的set方法,执行dep.notify()函数:

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中收集到的watcher实例,进行update()。也就是进行数据更新操作。这也就是简单的数据响应式,其实还需要注意的是: 当数据的getter触发后,会收集依赖,但也不是所有的触发方式都会收集依赖,只有通过watcher触发的getter会收集依赖:if (Dep.target) { dep.depend() },而所谓的被收集的依赖就是当前watcher,DOM中的数据必须通过watcher来绑定,只通过watcher来读取。

接下来进入下篇