Vue 源码阅读记录,MVVM 核心精读


要写 Vue.js 的源码分析,可以写一个系列。但是大体上,都知道了 Vue.js 的核心有那些东西。最重要,莫过于依赖收集了。在开始之前,这篇文章要回答如下几个问题。

PS: 内容有点长,看核心部分即可。而核心问题,看看下面的 QA。

  • Vue 是什么?

    Vue 是 JavaScript 的一款 MVVC 框架,从2014年由作者 Evan You 推出以来以简单易上手著称,搭配 options object style API 和易于理解的文档。在国内外迅速收到了一大片 star,成为了 GitHub 社区的明星项目。

    其组成部分如下:

    • MVVM = Observer + Dep + Watcher
    • VirtualDOM = (VNode = instance.render()) + Update(Patch(diff(newVNode, oldVNode)))
    • Component = new Vue(ComponentOptions) + VirtualDOM
    • ComponentOptions = name + filters + mixins + filters + components + props + data + computed + watch + provide + inject + methods + LifeCycle + template/render
    • Vue.compile/vue-template-compiler - 模版编译
  • 什么是 MVVM 以及 Vue 如何实现 MVVM?

    Model-View-ViewModel 是一种软件架构,我的理解是 MVC/P(Model-View-Controller)的进化版。

    从分层的角度去看,最上层是 View,中间层是 Controller/Presenter/ViewModel, 最下层就是 Model。

    Model 声明数据模型,View 声明视图,ViewModel 声明视图与数据模型之间的绑定关系

    当 Model 更新时 ViewModel 也被框架随之更新。这样做的好处是进一步解藕,将数据与视图的绑定关系抽象成 ViewModel 的 directives。通过对 Model 的赋值更新 View ,而无需手动编写 controller 更新 view。

    Vue 通过数据劫持实现 MVVM 架构。其核心是 Observer、Dep 和 Watcher 三个构造函数。component options 声明中的 data 即 reactive object 就是 Model,而 template 就是 View,其中的指令 v-bind , v-for 等就是 ViewModel 的绑定关系的实现。

  • Vue 如何实现依赖收集?

    1. Observer 使用 ES5 的 Object.defineProperty API 生成响应式对象(Reactive Object),给对象的每个 key 声明 getter/setter 劫持函数。
    2. 当访问响应式对象属性时,会进入 getter 劫持函数的逻辑。
      1. 如果存在有效的 Dep.target(即当前收集依赖的 watcher),
      2. 调用 watcher.addDep(dep) 收集当前属性的 dep 对象作为 watcher 的依赖,
      3. 同时执行 dep.addSub(this) 收集 watcher 作为 dep 的 subscribes 订阅者。
    3. 当对响应式对象的属性赋值时,会进入 setter 劫持函数的逻辑。
      1. 执行 dep.notify() 通知 dep.subs 中所有的订阅者 watcher,执行 watcher.update() 更新之前访问响应式对象属性 getter 时收集的所有 deps 依赖,
      2. 如果 watcher 不是 sync 同步执行的,则会被执行 queueWatcher(this) 放进队列中在 nextTick 后执行,
        1. watcher 在 computed 属性中是 lazy 的,不会立即计算其值,而是在执行计算属性 computed property 的 getter 时才执行 watcher.get() 方法获取 value
        2. 每次在执行 watcher.get() 方法前,会将当前 watcher 赋值为 [Dep.target](http://dep.target) (即收集依赖的目标 watcher 对象),然后才执行 watcher.getter() 方法计算/获取 watcher.value(以此在执行过程中收集依赖),当执行完 getter 函数获取懂啊 value 的值之后,执行 watcher.cleanupDeps() 方法给 depIds, deps 与 newDepIds, newDeps 进行换位(赋值 depIds 与 deps 为 newDepIds 与 newDeps),并清空 newDepIds 与 newDeps (用于 addDep 时进行是否已收集过该依赖的比对)。
      3. queueWatcher 对所有的 watcher 按顺序(以 id 升序) 排序后,执行 watcher.run() 方法,执行 this.cb.call(this.vm, value, oldValue) 方法(即传入新值、旧值并在当前 vm 上下文执行回调) 。
  • Vue 如何处理 data, computed, watch?

    在 Vue.prototype._init 函数中执行了 initState, 在 initState 中分别执行 initProps, initMethods, initData, initComputed, initWatch。让我们先看看 initData 的实现:

    1. initData 时,执行 getData 函数,将返回值赋值给 vm._data 属性,
    2. 然后通过 proxy 将 data 对象中各属性的 key 的代理到了 vm 实例上访问,这样可以直接通过 this[key] 读取,但访问的实质上是 vm._data[key]
    3. 最后 observe(data) 对象,添加观察的 getter/setter。然后访问实例属性时,返回它的值。

    而 initComputed 则使用到了其 MVVM 的核心之 Watcher,看看 initComputed 做了什么:

    1. 生成私有属性 watchers = vm._computedWatchers = {} 记录所有的 computed property
    2. for 循环 computed 中的 key 取对应的 getter/setter 生成实例化 watcher 的 options
    3. watchers[key] = new Watcher,为每个 computed 属性创建 watcher 实例
    4. 通过 defineComputed 函数为每个当前的每个 key 生成 createComputedGetter 到 vm 上
    5. 并在访问该属性时执行这个 computed getter,返回最新的经计算后的值

    initWatch 的实现:

    1. for 了一遍 $options.watch 对象然后为每个 key 执行 createWatcher(vm, key, handler) 函数,其中 handler 的值为 watch[key]
    2. createWatcher 则执行了 Vue.prototoype.$watch 方法 new Watcher(vm, expOrFn, cb, options)
    3. 判断声明 watcher 的 options 是否 options.immediate 如果为 true 则立即执行 cb 即 handler, 也就是 watch[key]
    4. 返回 unwatchFn 取消当前 watcher 的 all dependencies' subscriber
  • Watcher 被实例化的几个场景?

    1. initComputed 函数中实例化了 watcher 对象,用于监听 computed 中属性的变化,并重新执行 watcher.get() (用户声明 computedProperty 时的 getter 函数)返回的新的计算后的值。

      		var userDef = computed[key];
          var getter = typeof userDef === 'function' ? userDef : userDef.get
      
      		watchers[key] = new Watcher(
                vm,
                getter || noop,
                noop,
                computedWatcherOptions
              )
      
    2. initWatchVue.prototype.$watch中实例化了 watcher 对象,实例化用户手动添加的 watcher

      Vue.prototype.$watch = function (
            expOrFn,
            cb,
            options
          ) {
            var vm = this;
            if (isPlainObject(cb)) {
              return createWatcher(vm, expOrFn, cb, options)
            }
            options = options || {};
            options.user = true;
            var watcher = new Watcher(vm, expOrFn, cb, options);
            if (options.immediate) {
              try {
                cb.call(vm, watcher.value);
              } catch (error) {
                handleError(error, vm, ("callback for immediate watcher \"" + (watcher.expression) + "\""));
              }
            }
            return function unwatchFn () {
              watcher.teardown();
            }
          };
      
    3. mountComponent 函数中实例化了 watcher 对象。当 render 函数依赖的 reactive property 变化时,触发 watcher.update() 的同时执行 vm._update 函数走 VNode 的 Diff patch 方法。

      		const updateComponent = function () {
      	    vm._update(vm._render(), hydrating);
          };
      
          // we set this to vm._watcher inside the watcher's constructor
          // since the watcher's initial patch may call $forceUpdate (e.g. inside child
          // component's mounted hook), which relies on vm._watcher being already defined
          new Watcher(vm, updateComponent, noop, {
            before: function before () {
              if (vm._isMounted && !vm._isDestroyed) {
                callHook(vm, 'beforeUpdate');
              }
            }
          }, true /* isRenderWatcher */);
      
  • VueRouter 在何处被触发 RouterView 的更新?

Vue 源码解析

解析版本为 Vue.js 2.6.14,为 V2+ 最新版。一行一行,精读细读。这世界上 star 前五的框架之一,究竟有何神奇魔力?让我来轻轻的揭开它神秘的面纱。

Vue 构造函数

vuejs/vue

function Vue (options) {
  if (process.env.NODE_ENV !== 'production' &&
    !(this instanceof Vue)
  ) {
    warn('Vue is a constructor and should be called with the `new` keyword')
  }
	// 执行 _init 函数,传入 options
  this._init(options) {1}
}

// 注入各种 mixin 到 Vue.prototype 原型对象上

// 这就是 {1} 中的 `Vue.prototype._init` 实现
// 这里面初始化了 lifecycle, events, render, injections, state, provide
// 调用了 beforeCreate & created hook
initMixin(Vue) 

// 混入 $data, $props 和 $watch
stateMixin(Vue) 
eventsMixin(Vue)
lifecycleMixin(Vue)
renderMixin(Vue)

让我们先从 initMixin 开始看起:

niexport function initMixin (Vue: Class) {
	// 写入 _init 方法,即上面提到的 initMixin 和 构造函数中执行的 `this._init(options)`
  Vue.prototype._init = function (options?: Object) {
    const vm: Component = this
    // a uid
    vm._uid = uid++ // Vuejs 内部组件的 UUID

    let startTag, endTag
    
		// 开启性能监听,使用浏览器的 performance API
    if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
      startTag = `vue-perf-start:${vm._uid}`
      endTag = `vue-perf-end:${vm._uid}`

			// const perf = inBrowser && window.performance
			// mark = tag => perf.mark(tag)
      mark(startTag)
    }

    // a flag to avoid this being observed
		// 不监听 Vue 实例
    vm._isVue = true

    // merge options
    if (options && options._isComponent) {
      // optimize internal component instantiation
      // since dynamic options merging is pretty slow, and none of the
      // internal component options needs special treatment.
      initInternalComponent(vm, options)
    } else {
			// 初始化实例的 $options 属性
			// 它等于合并了 super.options(超类 options) + options(当前入参 options) + vm(当前实例属性)
      vm.$options = mergeOptions(
        resolveConstructorOptions(vm.constructor),
        options || {},
        vm
      )
    }
    /* istanbul ignore else */
    if (process.env.NODE_ENV !== 'production') {
			// 为 vm 中的属性访问增加 has, get, set 的 handler
			// 判断是否是保留字,是否为内部属性,是否存在未定义响应式属性
      initProxy(vm)
    } else {
      vm._renderProxy = vm
    }
    // expose real self
    vm._self = vm

		// 内部实现了大量跟 ComponentOptions 有关的函数
    initLifecycle(vm)
    initEvents(vm)
    initRender(vm)
    callHook(vm, 'beforeCreate')
    initInjections(vm) // resolve injections before data/props
    initState(vm)
    initProvide(vm) // resolve provide after data/props
    callHook(vm, 'created')

    /* istanbul ignore if */
    if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
      vm._name = formatComponentName(vm, false)
      mark(endTag)
      measure(`vue ${vm._name} init`, startTag, endTag)
    }

    if (vm.$options.el) {
      vm.$mount(vm.$options.el)
    }
  }
}

export function initInternalComponent (vm: Component, options: InternalComponentOptions) {
	// 赋值当前组件的 $options 记录组件自身的 $options 选项
	// $options 继承自 `vm.constructor.options` => defaultVueConstructorOptions?
  const opts = vm.$options = Object.create(vm.constructor.options)
  // doing this because it's faster than dynamic enumeration.
  const parentVnode = options._parentVnode
  opts.parent = options.parent
  opts._parentVnode = parentVnode

	// TODO:这一段没看明白???!!!
	// 赋值 parentVnode.componentOptions 到 opts._parent 属性上?
  const vnodeComponentOptions = parentVnode.componentOptions
  opts.propsData = vnodeComponentOptions.propsData
  opts._parentListeners = vnodeComponentOptions.listeners
  opts._renderChildren = vnodeComponentOptions.children
  opts._componentTag = vnodeComponentOptions.tag

	// 使用传入的 options.render 函数作为 render 方法
  if (options.render) {
    opts.render = options.render
    opts.staticRenderFns = options.staticRenderFns
  }
}

/**
 * Merge two option objects into a new one.
 * Core utility used in both instantiation and inheritance.
 */
export function mergeOptions (
  parent: Object,
  child: Object,
  vm?: Component // 当前实例
): Object {
  if (process.env.NODE_ENV !== 'production') {
		// 检查 child 的组件名是否合法
    checkComponents(child)
  }

	// TODO: 什么情况下 child 是个函数?
	// 如果是个函数,就取 child.options 为 child
  if (typeof child === 'function') {
    child = child.options
  }

	// 这里做的都是对各种语法对兼容和适配
  normalizeProps(child, vm) // 拦截校验、并重新赋值 options.props
  normalizeInject(child, vm) // 拦截校验、并重新赋值 options.inject
  normalizeDirectives(child) // 拦截校验、并重新赋值 options.directives

  // Apply extends and mixins on the child options,
  // but only if it is a raw options object that isn't
  // the result of another mergeOptions call.
  // Only merged options has the _base property.
  if (!child._base) {
		// 合并extends
    if (child.extends) {
      parent = mergeOptions(parent, child.extends, vm)
    }
		// 合并mixins
    if (child.mixins) {
      for (let i = 0, l = child.mixins.length; i < l; i++) {
				// merge each mixin use mergeOption method
        parent = mergeOptions(parent, child.mixins[i], vm)
      }
    }
  }

  const options = {}
  let key
  for (key in parent) {
    mergeField(key)
  }
  for (key in child) {
    if (!hasOwn(parent, key)) {
      mergeField(key)
    }
  }

	// 运行特定的合并策略
	// https://github.com/vuejs/vue/blob/612fb89547711cacb030a3893a0065b785802860/src/core/util/options.js
  function mergeField (key) {
    const strat = strats[key] || defaultStrat
    options[key] = strat(parent[key], child[key], vm, key)
  }
  return options
}

/**
 * Default strategy.
 */
const defaultStrat = function (parentVal: any, childVal: any): any {
  return childVal === undefined
    ? parentVal
    : childVal
}

/**
 * Validate component names
 */
function checkComponents (options: Object) {
	// 遍历当前实例 componentOptions 中 components 属性
  for (const key in options.components) {
    validateComponentName(key)
  }
}

// 验证组件名称是否符合 HTML5 specification 中的 custom element name
// 验证是否是 isBuiltInTag 或者 config.isReservedTag
export function validateComponentName (name: string) {
  if (!new RegExp(`^[a-zA-Z][\\-\\.0-9_${unicodeRegExp.source}]*$`).test(name)) {
    warn(
      'Invalid component name: "' + name + '". Component names ' +
      'should conform to valid custom element name in html5 specification.'
    )
  }
  if (isBuiltInTag(name) || config.isReservedTag(name)) {
    warn(
      'Do not use built-in or reserved HTML elements as component ' +
      'id: ' + name
    )
  }
}

/**
 * Ensure all props option syntax are normalized into the
 * Object-based format.
 */
function normalizeProps (options: Object, vm: ?Component) {
  const props = options.props
  if (!props) return
  const res = {}
  let i, val, name
	// 将数组的 props: string[] 转为 对象驼峰 res[name] = {type: null}
  if (Array.isArray(props)) {
    i = props.length
    while (i--) {
      val = props[i]
      if (typeof val === 'string') {
        name = camelize(val)
        res[name] = { type: null }
      } else if (process.env.NODE_ENV !== 'production') {
        warn('props must be strings when using array syntax.')
      }
    }
  } else if (isPlainObject(props)) {
		// 如果是个对象声明则转一下
    for (const key in props) {
      val = props[key] // key's value
      name = camelize(key) // camelizedKey
      res[name] = isPlainObject(val)
        ? val
        : { type: val } // 如果不是对象则转为对象 { type: val }
    }
  } else if (process.env.NODE_ENV !== 'production') {
    warn(
      `Invalid value for option "props": expected an Array or an Object, ` +
      `but got ${toRawType(props)}.`,
      vm
    )
  }

	// 重新赋值 options.props
  options.props = res
}

/**
 * Normalize all injections into Object-based format
 */
function normalizeInject (options: Object, vm: ?Component) {
  const inject = options.inject
  if (!inject) return
  const normalized = options.inject = {}
  if (Array.isArray(inject)) {
    for (let i = 0; i < inject.length; i++) {
      normalized[inject[i]] = { from: inject[i] }
    }
  } else if (isPlainObject(inject)) {
    for (const key in inject) {
      const val = inject[key]
      normalized[key] = isPlainObject(val)
        ? extend({ from: key }, val)
        : { from: val }
    }
  } else if (process.env.NODE_ENV !== 'production') {
    warn(
      `Invalid value for option "inject": expected an Array or an Object, ` +
      `but got ${toRawType(inject)}.`,
      vm
    )
  }
}

/**
 * Normalize raw function directives into object format.
 */
function normalizeDirectives (options: Object) {
  const dirs = options.directives
  if (dirs) {
    for (const key in dirs) {
      const def = dirs[key]
      if (typeof def === 'function') {
        dirs[key] = { bind: def, update: def }
      }
    }
  }
}

// 返回构造函数 options
export function resolveConstructorOptions (Ctor: Class) {
  let options = Ctor.options

	// TODO: 如果是 super,即非 Vue.extend 来的组件构造函数
	// https://github.com/vuejs/vue/blob/0603ff695d2f41286239298210113cbe2b209e28/src/core/global-api/extend.js#L43
	// Sub['super'] = Super
  if (Ctor.super) {
		// 如果来自超类,则继续递归获取超类的 options
    const superOptions = resolveConstructorOptions(Ctor.super)

		// https://github.com/vuejs/vue/blob/0603ff695d2f41286239298210113cbe2b209e28/src/core/global-api/extend.js#L73
		// Sub.superOptions = Super.options
    const cachedSuperOptions = Ctor.superOptions

		// 引用不同?为何会引用不同?
    if (superOptions !== cachedSuperOptions) {
      // super option changed,
      // need to resolve new options.
      Ctor.superOptions = superOptions
      // check if there are any late-modified/attached options (#4976)
      const modifiedOptions = resolveModifiedOptions(Ctor)
      // update base extend options
      if (modifiedOptions) {
				// 拷贝 modifiedOptions 到 Ctor.extendOptions
        extend(Ctor.extendOptions, modifiedOptions)
      }

			// 重新合并 Ctor.options,类似:
			// https://github.com/vuejs/vue/blob/0603ff695d2f41286239298210113cbe2b209e28/src/core/global-api/extend.js#L39
      options = Ctor.options = mergeOptions(superOptions, Ctor.extendOptions)
      if (options.name) {
        options.components[options.name] = Ctor
      }
    }
  }

  return options
}

function resolveModifiedOptions (Ctor: Class): ?Object {
  let modified
	// 构造函数的 options
	// https://github.com/vuejs/vue/blob/0603ff695d2f41286239298210113cbe2b209e28/src/core/global-api/extend.js#L39
	// Sub.options = mergeOptions(Super.options, extendOptions)
	// latest 即当下 Ctor 的 options
  const latest = Ctor.options

	// https://github.com/vuejs/vue/blob/0603ff695d2f41286239298210113cbe2b209e28/src/core/global-api/extend.js#L75
	// Sub.sealedOptions = extend({}, Sub.options)
  const sealed = Ctor.sealedOptions

  for (const key in latest) {
    if (latest[key] !== sealed[key]) {
      if (!modified) modified = {}
      modified[key] = latest[key]
    }
  }

  return modified
}

我们看到这里有几个生命周期方法,一个个去看看:

		initLifecycle(vm) // {1}
    initEvents(vm) // {2}
    initRender(vm) // {3}
    callHook(vm, 'beforeCreate')
    initInjections(vm) // {4} resolve injections before data/props
    initState(vm) // {5} resolve inner state
    initProvide(vm) // {6} resolve provide after data/props
    callHook(vm, 'created')

{1} initLifecycle 方法,顾名思义,应该是初始化 lifeCycle 生命周期相关的:

export function initLifecycle (vm: Component) {
  const options = vm.$options

  // locate first non-abstract parent
  let parent = options.parent
	// 存在 parent 并且非 options.abstract 抽象组件
  if (parent && !options.abstract) {
    while (parent.$options.abstract && parent.$parent) {
      parent = parent.$parent
    }

		// 找到非抽象组件后,将当前组件实例传给 `parent.$children`
    parent.$children.push(vm)
  }

	// 赋值 $parent 属性,挂载 $parent 的引用
  vm.$parent = parent
	// 挂载 $root 根节点的引用
  vm.$root = parent ? parent.$root : vm

	// 赋值 $children = [] 为空数组
  vm.$children = []
	// 赋值 $refs 为空对象
  vm.$refs = {}

	// 相关私有状态的赋值
  vm._watcher = null
  vm._inactive = null
  vm._directInactive = false

	// 已装载 = false
  vm._isMounted = false
	// 已销毁 = false
  vm._isDestroyed = false
	// 正在销毁中 = false
  vm._isBeingDestroyed = false
}

{2} 接下来是 initEvents(vm) 方法,顾名思义看起来是初始化事件相关函数。

export function initEvents (vm: Component) {
	// 挂载 _events 声明,是 pub-sub 实现中存储 listeners 的对象
  vm._events = Object.create(null)
	// TODO: What `_hasHookEvent` did?
  vm._hasHookEvent = false
  // init parent attached events
	// 初始化在 parent 中声明的 listeners
  const listeners = vm.$options._parentListeners
  if (listeners) {
		// 更新 parentListeners 到当前实例 vm
    updateComponentListeners(vm, listeners)
  }
}

let target: any

// pub-sub 的 on 实现,即添加 event listener
function add (event, fn) {
  target.$on(event, fn)
}

// pub-sub 的 off 实现,即移除 event listener
function remove (event, fn) {
  target.$off(event, fn)
}

// only execute event listener once 
function createOnceHandler (event, fn) {
	// 闭包保存当前的 target 引用
  const _target = target
  return function onceHandler () {
		// 一旦函数执行完成
    const res = fn.apply(null, arguments)
		// 且返回了有效(truly)的res
    if (res !== null) {
			// 则执行 off 卸载掉该 onceHandler 函数
      _target.$off(event, onceHandler)
    }
  }
}

export function updateComponentListeners (
  vm: Component,
  listeners: Object,
  oldListeners: ?Object
) {
	// 赋值 target
  target = vm
	// 开始更新 listeners
  updateListeners(listeners, oldListeners || {}, add, remove, createOnceHandler, vm)
	// 更新完成事件 listeners 后释放 target 引用
  target = undefined
}

export function updateListeners (
  on: Object,
  oldOn: Object,
  add: Function,
  remove: Function,
  createOnceHandler: Function,
  vm: Component
) {
  let name, def, cur, old, event
  for (name in on) {
    def = cur = on[name] // current event listener
    old = oldOn[name] // old event listener
    event = normalizeEvent(name) // normalize event by name
    /* istanbul ignore if */
		// __WEEX__ 环境变量就过了,暂时忽略
    if (__WEEX__ && isPlainObject(def)) {
      cur = def.handler
      event.params = def.params
    }
		// warning when `curr` is undefined
    if (isUndef(cur)) {
      process.env.NODE_ENV !== 'production' && warn(
        `Invalid handler for event "${event.name}": got ` + String(cur),
        vm
      )
    } else if (isUndef(old)) { // when `old` is undefined. 即不存在旧的事件定义时
      if (isUndef(cur.fns)) {
				// 重新声明并赋值 curr 和 on[name] 为新的 FnInvoker
        cur = on[name] = createFnInvoker(cur, vm)
      }
      if (isTrue(event.once)) {
				// 添加 onceHandler,TODO: 为什么这里有第三个参数?原函数中并未声明
        cur = on[name] = createOnceHandler(event.name, cur, event.capture)
      }

			// 将当前函数添加到 target(即当前vm) 中,TODO:后面这几个参数又在哪里用了?
      add(event.name, cur, event.capture, event.passive, event.params)
    } else if (cur !== old) {
			// 如果两者引用不等
      old.fns = cur // old.fns = cur 
      on[name] = old // 重新赋值 on[name] 引用到 old
    }
  }
  for (name in oldOn) {
		// 前面 for 了一遍 on, 现在 for 一遍 oldOn
    if (isUndef(on[name])) {
			// 如果 oldOn[name] 在新的 on[name] 中是未定义的
      event = normalizeEvent(name)
			// 则删除掉改事件
      remove(event.name, oldOn[name], event.capture)
    }
  }
}

// 通过入参事件名称 name 初始化事件描述符
const normalizeEvent = cached((name: string): {
  name: string,
  once: boolean,
  capture: boolean,
  passive: boolean,
  handler?: Function,
  params?: Array
} => {
  const passive = name.charAt(0) === '&'
  name = passive ? name.slice(1) : name
  const once = name.charAt(0) === '~' // Prefixed last, checked first
  name = once ? name.slice(1) : name
  const capture = name.charAt(0) === '!'
  name = capture ? name.slice(1) : name
  return {
    name, // &~!name ?!
    once,
    capture,
    passive
  }
})

// 工厂方法,返回绑定了 this 的 invoker function
// 感觉这个函数的核心目的:
// 1. 初始化 数据结构,返回 invoker 函数
// 2. 内部使用 invokeWithErrorHandling 执行函数
export function createFnInvoker (fns: Function | Array, vm: ?Component): Function {
  function invoker () {
    const fns = invoker.fns
    if (Array.isArray(fns)) {
      const cloned = fns.slice()
      for (let i = 0; i < cloned.length; i++) {
        invokeWithErrorHandling(cloned[i], null, arguments, vm, `v-on handler`)
      }
    } else {
      // return handler return value for single handlers
      return invokeWithErrorHandling(fns, null, arguments, vm, `v-on handler`)
    }
  }
  invoker.fns = fns
  return invoker
}

// 添加 handleError 错误追踪
export function invokeWithErrorHandling (
  handler: Function,
  context: any,
  args: null | any[],
  vm: any,
  info: string
) {
  let res
  try {
    res = args ? handler.apply(context, args) : handler.call(context)
		// 如果是 promise,则添加 .catch 并处理事件
		// 所以 res._handled 只是此处的 flag 用于判断是否添加 handleError 错误错误
    if (res && !res._isVue && isPromise(res) && !res._handled) {
      res.catch(e => handleError(e, vm, info + ` (Promise/async)`))
      // issue #9511
      // avoid catch triggering multiple times when nested calls
      res._handled = true
    }
  } catch (e) {
    handleError(e, vm, info)
  }
  return res
}

// 绑定当前出错 vm 堆栈提示
export function handleError (err: Error, vm: any, info: string) {
  // Deactivate deps tracking while processing error handler to avoid possible infinite rendering.
  // See: https://github.com/vuejs/vuex/issues/1505
  pushTarget()
  try {
    if (vm) {
      let cur = vm
      while ((cur = cur.$parent)) {
				// 递归执行 子 -> 父 的 errorCaptured hook 钩子函数
        const hooks = cur.$options.errorCaptured
        if (hooks) {
          for (let i = 0; i < hooks.length; i++) {
            try {
              const capture = hooks[i].call(cur, err, vm, info) === false
              if (capture) return
            } catch (e) {
              globalHandleError(e, cur, 'errorCaptured hook')
            }
          }
        }
      }
    }
    globalHandleError(err, vm, info)
  } finally {
    popTarget()
  }
}

// Vue 内部的全局错误处理 handler
function globalHandleError (err, vm, info) {
  if (config.errorHandler) {
    try {
			// 使用配置的 errorHandler
			// 比如 Sentry 等第三方错误监控系统就会使用此配置
      return config.errorHandler.call(null, err, vm, info)
    } catch (e) {
      // if the user intentionally(有意的) throws the original error in the handler,
      // do not log it twice
      if (e !== err) { // 这个判断很精髓
        logError(e, null, 'config.errorHandler')
      }
    }
  }
  logError(err, vm, info)
}

// 打印错误及报错组件的堆栈信息
function logError (err, vm, info) {
  if (process.env.NODE_ENV !== 'production') {
		// 看下方实现
    warn(`Error in ${info}: "${err.toString()}"`, vm)
  }
  /* istanbul ignore else */
	// 环境判断,浏览器或 weex 中且 console 不为 undefined 则 console.error
  if ((inBrowser || inWeex) && typeof console !== 'undefined') {
    console.error(err)
  } else {
    throw err
  }
}

// warn 在非 production 环境下的实现
warn = (msg, vm) => {
		// 注意此处的 generateComponentTrace
    const trace = vm ? generateComponentTrace(vm) : ''

    if (config.warnHandler) {
      config.warnHandler.call(null, msg, vm, trace)
    } else if (hasConsole && (!config.silent)) {
      console.error(`[Vue warn]: ${msg}${trace}`)
    }
  }

// 递归 vm = child -> parent 添加到 tree 中并在最后生成 log
generateComponentTrace = vm => {
    if (vm._isVue && vm.$parent) {
      const tree = []
			// 当前递归调用的次数
      let currentRecursiveSequence = 0
      while (vm) {
				// 一旦 tree 中已经有值,则进入下面检查
        if (tree.length > 0) {
          const last = tree[tree.length - 1]
					// 是否是同一个构造函数构造的实例
          if (last.constructor === vm.constructor) {
						// 如果是的话则此处是个递归
						// 此处没有 push vm 到 tree 中
            currentRecursiveSequence++
            vm = vm.$parent
            continue
          } else if (currentRecursiveSequence > 0) {
						// 如果递归结束了,则 last 变成一个数组对应下方的  Array.isArray(vm) 判断
						// [last, 递归调用次数] 对应下方 ${formatComponentName(vm[0])}... (${vm[1]} recursive calls)
            tree[tree.length - 1] = [last, currentRecursiveSequence]
						// 重置 currentRecursiveSequence
            currentRecursiveSequence = 0
          }
        }
        tree.push(vm)
        vm = vm.$parent
      }
      return '\n\nfound in\n\n' + tree
        .map((vm, i) => `${
          i === 0 ? '---> ' : repeat(' ', 5 + i * 2)
        }${
          Array.isArray(vm)
            ? `${formatComponentName(vm[0])}... (${vm[1]} recursive calls)`
            : formatComponentName(vm)
        }`)
        .join('\n')
    } else {
      return `\n\n(found in ${formatComponentName(vm)})`
    }
  }

{3} initRender(vm) 至此终于把上一个 initEvent 搞完了。开始新的一个函数,oh,render 这可是 jsx 中的核心函数了,看看 Vuejs 怎么初始化的吧。我有预感,这个比上面的复杂得多得多。

然而实际上并不复杂,initRender

export function initRender (vm: Component) {
	// _vnode 即 vnode 节点,初始化为空
  vm._vnode = null // the root of the child tree
	// TODO: What's `v-once` mean?
  vm._staticTrees = null // v-once cached trees
  const options = vm.$options
	// TODO: parentNode 是父节点?
  const parentVnode = vm.$vnode = options._parentVnode // the placeholder node in parent tree
	// parentNode.context 是父节点的渲染上下文,但具体是什么?
  const renderContext = parentVnode && parentVnode.context
	// _renderChildren?? 我的理解是所有的 children
  vm.$slots = resolveSlots(options._renderChildren, renderContext)
  vm.$scopedSlots = emptyObject
  // bind the createElement fn to this instance
  // so that we get proper render context inside it.
  // args order: tag, data, children, normalizationType, alwaysNormalize
  // internal version is used by render functions compiled from templates
  vm._c = (a, b, c, d) => createElement(vm, a, b, c, d, false)
  // normalization is always applied for the public version, used in
  // user-written render functions.
  vm.$createElement = (a, b, c, d) => createElement(vm, a, b, c, d, true)

  // $attrs & $listeners are exposed for easier HOC creation.
  // they need to be reactive so that HOCs using them are always updated
  const parentData = parentVnode && parentVnode.data

  /* istanbul ignore else */
  if (process.env.NODE_ENV !== 'production') {
		// 开发环境下,监听 $attrs 属性。定义 $attrs 是 parentVnode.data,且 readonly
		// defineReactive$$1(obj,key,val,customSetter,shallow) 
    defineReactive(vm, '$attrs', parentData && parentData.attrs || emptyObject, () => {
			// isUpdatingChildComponent 是一个 flag,用于判断当前是否正在更新组件
      !isUpdatingChildComponent && warn(`$attrs is readonly.`, vm)
    }, true)
		// 开发环境下,监听 $listeners 属性。定义 $listeners 是 options._parentListeners,且 readonly
    defineReactive(vm, '$listeners', options._parentListeners || emptyObject, () => {
      !isUpdatingChildComponent && warn(`$listeners is readonly.`, vm)
    }, true)
  } else {
		// 生产环节,不拦截 setter 不抛出 warning
    defineReactive(vm, '$attrs', parentData && parentData.attrs || emptyObject, null, true)
    defineReactive(vm, '$listeners', options._parentListeners || emptyObject, null, true)
  }
}

resolveSlots(vm) ,将 children 转换为 $slots 对象。

/**
 * Runtime helper for resolving raw children VNodes into a slot object.
 */
export function resolveSlots (
  children: ?Array,
  context: ?Component
): { [key: string]: Array } {
  if (!children || !children.length) {
    return {}
  }
  const slots = {}
  for (let i = 0, l = children.length; i < l; i++) {
    const child = children[i]
    const data = child.data
    // remove slot attribute if the node is resolved as a Vue slot node
    if (data && data.attrs && data.attrs.slot) {
      delete data.attrs.slot
    }
    // named slots should only be respected if the vnode was rendered in the
    // same context.
    if ((child.context === context || child.fnContext === context) &&
      data && data.slot != null
    ) {
      const name = data.slot
      const slot = (slots[name] || (slots[name] = []))
      if (child.tag === 'template') {
        slot.push.apply(slot, child.children || [])
      } else {
        slot.push(child)
      }
    } else {
      (slots.default || (slots.default = [])).push(child)
    }
  }

  // ignore slots that contains only whitespace
  for (const name in slots) {
    if (slots[name].every(isWhitespace)) {
      delete slots[name]
    }
  }
  return slots
}

function isWhitespace (node: VNode): boolean {
  return (node.isComment && !node.asyncFactory) || node.text === ' '
}

{4} 嗯,继续看看 initInjections 函数的实现:

	/**
   * In some cases we may want to disable observation inside a component's
   * update computation.
   */
  var shouldObserve = true

  function toggleObserving(value) {
    shouldObserve = value
  }

	function initInjections(vm) {
    var result = resolveInject(vm.$options.inject, vm)
    if (result) {
			// 无需观察,即无需收集依赖
      toggleObserving(false)
      Object.keys(result).forEach(function(key) {
        /* istanbul ignore else */
        {
          defineReactive$$1(vm, key, result[key], function() {
            warn(
              'Avoid mutating an injected value directly since the changes will be ' +
                'overwritten whenever the provided component re-renders. ' +
                'injection being mutated: "' +
                key +
                '"',
              vm
            )
          })
        }
      })
      toggleObserving(true)
    }
  }

	// 解析并返回 inject,如果 componentOptions 声明了 inject 对象
	// 在解析之前,当然要看看 normalize 喽,根据 Vuejs 的代码习惯,所有的这些东西
	// 都是先 normalized(这一步是为了兼容各种五花八门的写法)
	// 然后 resolved(这一步是为了才是正儿八经的从中取值,同时加了很多包装) 
	function resolveInject(inject, vm) {
    if (inject) {
      // inject is :any because flow is not smart enough to figure out cached
      var result = Object.create(null)
      var keys = hasSymbol ? Reflect.ownKeys(inject) : Object.keys(inject)

      for (var i = 0; i < keys.length; i++) {
        var key = keys[i]
        // #6574 in case the inject object is observed...
        if (key === '__ob__') {
          continue
        }
        var provideKey = inject[key].from
        var source = vm
				// 此处的 while 则是从 source 开始往上递归回溯
				// 直到找到了 _provided[provideKey] 的值,如果找到了当然就 break 了
        while (source) {
					// 我们看到了 `_provided` 关键字
          if (source._provided && hasOwn(source._provided, provideKey)) {
            result[key] = source._provided[provideKey]
            break
          }
          source = source.$parent
        }
				// 如果没有或者没找到 source,则取 default 为值了撒
        if (!source) {
          if ('default' in inject[key]) {
            var provideDefault = inject[key].default
            result[key] =
              typeof provideDefault === 'function'
                ? provideDefault.call(vm)
                : provideDefault
          } else {
						// 如果没有声明 default,则 warn
            warn('Injection "' + key + '" not found', vm)
          }
        }
      }
			// 返回解析后的结果
      return result
    }
  }

	/**
   * Normalize all injections into Object-based format
   */
  function normalizeInject(options, vm) {
    var inject = options.inject
    if (!inject) {
      return
    }
    var normalized = (options.inject = {})
    if (Array.isArray(inject)) {
      for (var i = 0; i < inject.length; i++) {
        normalized[inject[i]] = { from: inject[i] }
      }
    } else if (isPlainObject(inject)) {
      for (var key in inject) {
        var val = inject[key]
        normalized[key] = isPlainObject(val)
          ? extend({ from: key }, val)
          : { from: val }
      }
    } else {
      warn(
        'Invalid value for option "inject": expected an Array or an Object, ' +
          'but got ' +
          toRawType(inject) +
          '.',
        vm
      )
    }
  }

{6} 既然有 injection 那么一定有 provide 了撒:

	/*  */
	// 好奇,这玩意有没有 normalize function 呢,找了下,没有找到
  function initProvide(vm) {
    var provide = vm.$options.provide
    if (provide) {
      vm._provided = typeof provide === 'function' ? provide.call(vm) : provide
    }
  }

还有 {5} 就是initState(vm)

ep	function initState(vm) {
		// _watchers = [] 这一步在后面的使用中有深意
    vm._watchers = []
    var opts = vm.$options // Component Options
    if (opts.props) {
      initProps(vm, opts.props) // 初始化、校验、解析 props
    }
    if (opts.methods) {
      initMethods(vm, opts.methods) // 初始化、校验、解析 methods
    }
    if (opts.data) {
      initData(vm) // 初始化 data
    } else {
			// 如果没有 data 选项,则设置个 default data
      observe((vm._data = {}), true /* asRootData */)
    }
    if (opts.computed) {
      initComputed(vm, opts.computed) // 初始化 computed 属性,收集依赖
    }

		// Firefox has a "watch" function on Object.prototype...
		// var nativeWatch = {}.watch
    if (opts.watch && opts.watch !== nativeWatch) {
      initWatch(vm, opts.watch) // 初始化 watch 属性
    }
  }

	// 初始化 props,看里面主要做的事是遍历了一道 propsData
	function initProps(vm, propsOptions) {
    var propsData = vm.$options.propsData || {}
    var props = (vm._props = {})
    // cache prop keys so that future props updates can iterate using Array
    // instead of dynamic object key enumeration.
    var keys = (vm.$options._propKeys = [])
    var isRoot = !vm.$parent
    // root instance props should be converted
    if (!isRoot) {
      toggleObserving(false)
    }
    var loop = function(key) {
      keys.push(key)
			// 校验 prop 同时取出 value
      var value = validateProp(key, propsOptions, propsData, vm)
      /* istanbul ignore else */
      {
        var hyphenatedKey = hyphenate(key)
        if (
          isReservedAttribute(hyphenatedKey) ||
          config.isReservedAttr(hyphenatedKey)
        ) {
          warn(
            '"' +
              hyphenatedKey +
              '" is a reserved attribute and cannot be used as component prop.',
            vm
          )
        }
				// 定义成 reactive 属性
				// defineReactive$$1(obj, key, val, customSetter, shallow)
        defineReactive$$1(props, key, value, function() {
          if (!isRoot && !isUpdatingChildComponent) {
            warn(
              'Avoid mutating a prop directly since the value will be ' +
                'overwritten whenever the parent component re-renders. ' +
                "Instead, use a data or computed property based on the prop's " +
                'value. Prop being mutated: "' +
                key +
                '"',
              vm
            )
          }
        })
      }
      // static props are already proxied on the component's prototype
      // during Vue.extend(). We only need to proxy props defined at
      // instantiation here.
      if (!(key in vm)) {
        proxy(vm, '_props', key)
      }
    }

    for (var key in propsOptions) loop(key)
    toggleObserving(true)
  }

	function proxy(target, sourceKey, key) {
    sharedPropertyDefinition.get = function proxyGetter() {
      return this[sourceKey][key]
    }
    sharedPropertyDefinition.set = function proxySetter(val) {
      this[sourceKey][key] = val
    }
    Object.defineProperty(target, key, sharedPropertyDefinition)
  }

	function initMethods(vm, methods) {
    var props = vm.$options.props
    for (var key in methods) {
      {
        if (typeof methods[key] !== 'function') {
          warn(
            'Method "' +
              key +
              '" has type "' +
              typeof methods[key] +
              '" in the component definition. ' +
              'Did you reference the function correctly?',
            vm
          )
        }
        if (props && hasOwn(props, key)) {
          warn('Method "' + key + '" has already been defined as a prop.', vm)
        }
        if (key in vm && isReserved(key)) {
          warn(
            'Method "' +
              key +
              '" conflicts with an existing Vue instance method. ' +
              'Avoid defining component methods that start with _ or $.'
          )
        }
      }

			// 将 ComponentOptions 中所有的 methods 挂载到 vm 实例上
      vm[key] =
        typeof methods[key] !== 'function' ? noop : bind(methods[key], vm)
    }
  }

	/**
   * Check if a string starts with $ or _
   */
  function isReserved(str) {
    var c = (str + '').charCodeAt(0)
    return c === 0x24 || c === 0x5f
  }

	// 初始化 Data,这一步跟 Watcher 应该有关系才是
	function initData(vm) {
    var data = vm.$options.data
		// 赋值到 vm._data,执行 getData 方法(清空了 Dep.target 后调用 data 函数)
		// 然后使用 proxy 将属性代理到 vm 上但访问但实质上是 vm._data[key]
		// 最后 observe(data) 对象,添加观察
    data = vm._data =
      typeof data === 'function' ? getData(data, vm) : data || {}
    if (!isPlainObject(data)) {
      data = {}
      warn(
        'data functions should return an object:\n' +
          'https://vuejs.org/v2/guide/components.html#data-Must-Be-a-Function',
        vm
      )
    }
    // proxy data on instance
    var keys = Object.keys(data)
    var props = vm.$options.props
    var methods = vm.$options.methods
    var i = keys.length
    while (i--) {
      var key = keys[i]
      {
        if (methods && hasOwn(methods, key)) {
          warn(
            'Method "' + key + '" has already been defined as a data property.',
            vm
          )
        }
      }
      if (props && hasOwn(props, key)) {
        warn(
          'The data property "' +
            key +
            '" is already declared as a prop. ' +
            'Use prop default value instead.',
          vm
        )
      } else if (!isReserved(key)) {
        proxy(vm, '_data', key)
      }
    }
    // observe data, make data be reactively
    observe(data, true /* asRootData */)
  }

	// 执行 data 函数获取其返回数据
	function getData(data, vm) {
		// 执行前清空了 Dep.target,也就是说这一步不需要收集依赖
    // #7573 disable dep collection when invoking data getters
    pushTarget()
    try {
      return data.call(vm, vm)
    } catch (e) {
      handleError(e, vm, 'data()')
      return {}
    } finally {
      popTarget()
    }
  }

	// 初始化 computed 属性
	function initComputed(vm, computed) {
    // $flow-disable-line
		// 先创建和挂载私有属性 _computedWatchers
    var watchers = (vm._computedWatchers = Object.create(null))
    // computed properties are just getters during SSR
    var isSSR = isServerRendering()

		// for 循环 computed 中的 key 取/生成对应的 getter/setter
    for (var key in computed) {
      var userDef = computed[key]
      var getter = typeof userDef === 'function' ? userDef : userDef.get
      if (getter == null) {
        warn('Getter is missing for computed property "' + key + '".', vm)
      }

      if (!isSSR) {
        // create internal watcher for the computed property.
				// Watcher(vm, expOrFn, cb, options, isRenderWatcher)
        watchers[key] = new Watcher(
          vm,
          getter || noop,
          noop,
          computedWatcherOptions // { lazy: true }
					// this.value = this.lazy ? undefined : this.get()
        )
      }

      // component-defined computed properties are already defined on the
      // component prototype. We only need to define computed properties defined
      // at instantiation here.
      if (!(key in vm)) {
        defineComputed(vm, key, userDef)
      } else {
        if (key in vm.$data) {
          warn(
            'The computed property "' + key + '" is already defined in data.',
            vm
          )
        } else if (vm.$options.props && key in vm.$options.props) {
          warn(
            'The computed property "' + key + '" is already defined as a prop.',
            vm
          )
        }
      }
    }
  }

	function defineComputed(target, key, userDef) {
    var shouldCache = !isServerRendering() // true
    if (typeof userDef === 'function') {
      sharedPropertyDefinition.get = shouldCache
        ? createComputedGetter(key)
        : createGetterInvoker(userDef)
      sharedPropertyDefinition.set = noop
    } else {
      sharedPropertyDefinition.get = userDef.get
        ? shouldCache && userDef.cache !== false
          ? createComputedGetter(key)
          : createGetterInvoker(userDef.get)
        : noop
      sharedPropertyDefinition.set = userDef.set || noop
    }
    if (sharedPropertyDefinition.set === noop) {
      sharedPropertyDefinition.set = function() {
        warn(
          'Computed property "' +
            key +
            '" was assigned to but it has no setter.',
          this
        )
      }
    }
    Object.defineProperty(target, key, sharedPropertyDefinition)
  }

	function createComputedGetter(key) {
    return function computedGetter() {
      var watcher = this._computedWatchers && this._computedWatchers[key]
      if (watcher) {
				// in Watcher defintion: this.dirty = this.lazy // for lazy watchers
				// and the compouted properties are always `lazy` & `dirty`
        if (watcher.dirty) {
          watcher.evaluate()
        }
				// 为当前 computedGetter 收集 dep
        if (Dep.target) {
					// Depend on all deps collected by this watcher.
					// (for dep of watcher.deps) { dep.depend() }
          watcher.depend()
        }
        return watcher.value
      }
    }
  }

  function createGetterInvoker(fn) {
    return function computedGetter() {
      return fn.call(this, this)
    }
  }

	// 初始化 watch 属性
	function initWatch(vm, watch) {
    for (var key in watch) {
      var handler = watch[key]
      if (Array.isArray(handler)) {
        for (var i = 0; i < handler.length; i++) {
          createWatcher(vm, key, handler[i])
        }
      } else {
        createWatcher(vm, key, handler)
      }
    }
  }

  function createWatcher(vm, expOrFn, handler, options) {
    if (isPlainObject(handler)) {
      options = handler
      handler = handler.handler
    }

		// 所以 vue componentOptions.watcher 的 handler 可以声明成 string
		// 然后从 vm 上面取. okay, i got it. 
    if (typeof handler === 'string') {
      handler = vm[handler]
    }

		// 用的 vm.$watch 原型对象方法,让我们继续追踪
    return vm.$watch(expOrFn, handler, options)
  }

这儿是 stateMixin 方法,内部实现了 Vue.prototype.$watch

function stateMixin(Vue) {
    // flow somehow has problems with directly declared definition object
    // when using Object.defineProperty, so we have to procedurally build up
    // the object here.
    var dataDef = {}
		// 返回实例私有属性 _data
    dataDef.get = function() {
      return this._data
    }

		// 返回实例私有属性 _props
    var propsDef = {}
    propsDef.get = function() {
      return this._props
    }

		// 阻止对 data 和 props 的赋值重写
    {
      dataDef.set = function() {
        warn(
          'Avoid replacing instance root $data. ' +
            'Use nested data properties instead.',
          this
        )
      }
      propsDef.set = function() {
        warn('$props is readonly.', this)
      }
    }

		// 使用 $data 表明对 _data 私有属性的包装,增强了 getter/setter
    Object.defineProperty(Vue.prototype, '$data', dataDef)
		// 使用 $props 表明对 _props 私有属性的包装,增强了 getter/setter
    Object.defineProperty(Vue.prototype, '$props', propsDef)

		// 响应式的 $set & $delete
    Vue.prototype.$set = set // $set(target: Array|Object, key: string, val: any )
    Vue.prototype.$delete = del // $del(target: Array|Object, key: string)

		// 响应式的 $watch ??
    Vue.prototype.$watch = function(expOrFn, cb, options) {
      var vm = this
      if (isPlainObject(cb)) {
        return createWatcher(vm, expOrFn, cb, options)
      }
      options = options || {}
      options.user = true // it means this watcher was from user declare manual 
			// 此处的 watcher 会立即执行,因为非 lazy
			// 会立即执行 this.value = this.get() 并开始依赖收集
      var watcher = new Watcher(vm, expOrFn, cb, options)
			
			// 如果传入的 watch options.immediate 则立即执行回调
      if (options.immediate) {
        try {
					// 立即执行 cb
          cb.call(vm, watcher.value)
        } catch (error) {
          handleError(
            error,
            vm,
            'callback for immediate watcher "' + watcher.expression + '"'
          )
        }
      }

      return function unwatchFn() {
        watcher.teardown()
      }
    }
  }

	/**
   * Set a property on an object. Adds the new property and
   * triggers change notification if the property doesn't
   * already exist.
   */
  function set(target, key, val) {
    if (isUndef(target) || isPrimitive(target)) {
      warn(
        'Cannot set reactive property on undefined, null, or primitive value: ' +
          target
      )
    }
    if (Array.isArray(target) && isValidArrayIndex(key)) {
      target.length = Math.max(target.length, key)
			// 执行数组的包装方法,splice
      target.splice(key, 1, val)
      return val
    }
		// 如果 key 已存在 target 中
    if (key in target && !(key in Object.prototype)) {
      target[key] = val
      return val
    }
    var ob = target.__ob__
    if (target._isVue || (ob && ob.vmCount)) {
      warn(
        'Avoid adding reactive properties to a Vue instance or its root $data ' +
          'at runtime - declare it upfront in the data option.'
      )
      return val
    }
    if (!ob) {
      target[key] = val
      return val
    }
    defineReactive$$1(ob.value, key, val)
    ob.dep.notify()
    return val
  }

  /**
   * Delete a property and trigger change if necessary.
   */
  function del(target, key) {
    if (isUndef(target) || isPrimitive(target)) {
      warn(
        'Cannot delete reactive property on undefined, null, or primitive value: ' +
          target
      )
    }
    if (Array.isArray(target) && isValidArrayIndex(key)) {
      target.splice(key, 1)
      return
    }
    var ob = target.__ob__
    if (target._isVue || (ob && ob.vmCount)) {
      warn(
        'Avoid deleting properties on a Vue instance or its root $data ' +
          '- just set it to null.'
      )
      return
    }
    if (!hasOwn(target, key)) {
      return
    }
    delete target[key]
    if (!ob) {
      return
    }
    ob.dep.notify()
  }

这是 vue.js 内部的 event modifer 的状态捕获实现
https://vuejs.org/v2/guide/events.html#Event-Modifiers

精炼源码

上面的走远了,逐行读 Vue.js 代码并未带给我任何收益,反而浪费了许多时间,甚至带来了负面情绪拖延。

还是从核心方法上来看起走吧。

我觉得 Vue.js 之所以四不像的原因就是它什么都有。它就是 [MVVM, Component, VirtualDOM, Filters, Template, Directives, Provide/Inject ] 的集合,而且各种吸收,有点营养过剩。

而 React 就比较简单,就是 UI = React(state)。而 Vue 就是 MVVM JS SDK.

MVVM

Dep + Observer + Watcher/Subscriber 实现了 Vuejs 的 MVVM。

先看 Dep 的源码:

	/*  */
  var uid = 0;

  /**
   * A dep is an observable that can have multiple
   * directives subscribing to it.
   */
  var Dep = function Dep () {
    this.id = uid++;
    this.subs = [];
  };

	/**
	 * @param {Watcher} sub
	 */
  Dep.prototype.addSub = function addSub (sub) {
    this.subs.push(sub);
  };

  Dep.prototype.removeSub = function removeSub (sub) {
    remove(this.subs, sub);
  };

	// Watcher 收集依赖 Deps => wathcher.deps
  Dep.prototype.depend = function depend () {
    if (Dep.target) {
			// 给当前 Dep.target 的 Watcher 添加 dep 实例
      Dep.target.addDep(this);
    }
  };

  Dep.prototype.notify = function notify () {
    // stabilize the subscriber list first
    var subs = this.subs.slice();
    if (!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(function (a, b) { return a.id - b.id; });
    }
    for (var i = 0, l = subs.length; i < l; i++) {
      subs[i].update();
    }
  };

  // The current target watcher being evaluated.
  // This is globally unique because only one watcher
  // can be evaluated at a time.
  Dep.target = null; // Dep.target 是个 Watcher
  var targetStack = [];

  function pushTarget (target) {
    targetStack.push(target);
    Dep.target = target;
  }

  function popTarget () {
    targetStack.pop();
    Dep.target = targetStack[targetStack.length - 1];
  }

Observer 的源码:

	/**
   * Observer class that is attached to each observed
   * object. Once attached, the observer converts the target
   * object's property keys into getter/setters that
   * collect dependencies and dispatch updates.
   */
  var Observer = function Observer (value) {
    this.value = value;
    this.dep = new Dep(); // __ob__.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.
   */
  Observer.prototype.walk = function walk (obj) {
    var keys = Object.keys(obj);
    for (var i = 0; i < keys.length; i++) {
      defineReactive$$1(obj, keys[i]);
    }
  };

  /**
   * Observe a list of Array items.
   */
  Observer.prototype.observeArray = function observeArray (items) {
    for (var i = 0, l = items.length; i < l; i++) {
      observe(items[i]);
    }
  };

  // helpers

  /**
   * Augment a target Object or Array by intercepting
   * the prototype chain using __proto__
   */
  function protoAugment (target, src) {
    /* eslint-disable no-proto */
    target.__proto__ = src;
    /* eslint-enable no-proto */
  }

  /**
   * Augment a target Object or Array by defining
   * hidden properties.
   */
  /* istanbul ignore next */
  function copyAugment (target, src, keys) {
    for (var i = 0, l = keys.length; i < l; i++) {
      var key = keys[i];
      def(target, key, src[key]);
    }
  }

  /**
   * 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.
   */
  function observe (value, asRootData) {
    if (!isObject(value) || value instanceof VNode) {
      return
    }
    var ob;
    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
  }

  /**
   * Define a reactive property on an Object.
   */
  function defineReactive$$1 (
    obj,
    key,
    val,
    customSetter,
    shallow
  ) {
    var dep = new Dep();

    var property = Object.getOwnPropertyDescriptor(obj, key);
    if (property && property.configurable === false) {
      return
    }

    // cater for pre-defined getter/setters
    var getter = property && property.get;
    var setter = property && property.set;

		// defineReactive$$1(obj,key) 的情况下编程式的为 val 赋值
    if ((!getter || setter) && arguments.length === 2) {
      val = obj[key];
    }

    var childOb = !shallow && observe(val);
    Object.defineProperty(obj, key, {
      enumerable: true,
      configurable: true,
      get: function reactiveGetter () {
				// 此处的关键是,生命了 getter 函数,但只有访问这个属性时
				// 且 Dep.target 存在时才会开始依赖收集
        var value = getter ? getter.call(obj) : val;

				// 执行 getter 时如果当前存在 Dep.target Watcher 说明
				// 需要执行所有对该 Watcher 的 deps 以更新所有 subscribe
        if (Dep.target) {
          dep.depend(); // 收集依赖 => Dep.target.addDep(this) TODO:这一步需要结合下面的 Watcher 看内部具体做了哪些事情

          if (childOb) {
						// Deep Watch, 功能同上 TODO: 同上所述
            childOb.dep.depend();
            if (Array.isArray(value)) {
							// Collect dependencies
              dependArray(value);
            }
          }
        }
        return value
      },
      set: function reactiveSetter (newVal) {
				// 从 getter 取值,因为 getter 可能依赖了某些 reactive object property
				// TODO: 如果 getter 返回了最新的值的话,不就有问题了吗?
				// 说明这时候 getter 的返回值并不会发生变化
				// 如果没有 getter,就从闭包中取上次的 val
        var value = getter ? getter.call(obj) : val;
        /* eslint-disable no-self-compare */
				// diff 对比
        if (newVal === value || (newVal !== newVal && value !== value)) {
          return
        }
        /* eslint-enable no-self-compare */
				// TODO:自定义 setter,执行这个的意义还不太明白。
        if (customSetter) {
          customSetter();
        }
        // #7981: for accessor properties without setter
				// TODO: 有 getter 没有 setter 就返回?
        if (getter && !setter) { return }
				// 有 setter 的话直接执行 setter,这适合 computed property: { get, set } 情形
        if (setter) {
          setter.call(obj, newVal);
        } else {
					// 更新闭包中 val 的值,用于下次比对
          val = newVal;
        }

				// 重新赋值 child Observer 对象在闭包中的值
        childOb = !shallow && observe(newVal);
				// Setter 赋值之后按顺序触发所有 Watchers 更新 update()
        dep.notify();
      }
    });
  }

	/**
   * Collect dependencies on array elements when the array is touched, since
   * we cannot intercept array element access like property getters.
   */
  function dependArray (value) {
    for (var e = (void 0), i = 0, l = value.length; i < l; i++) {
      e = value[i];
      e && e.__ob__ && e.__ob__.dep.depend();
      if (Array.isArray(e)) {
        dependArray(e);
      }
    }
  }

上面的代码给我影响深刻的是 Observer 所有的 getter 都是 Dep.depend() ? Watcher.addDep(this) 的实现,而 setter 则是 Dep.notify() 通知所有更新,并同时执行所有 Watcher 的后续逻辑。

而 Vue.prototype.set(obj, lkey, val) 则跟 defineReactive 是包装的实现,只是做了写判断,del 方法同理。

来看看 Watcher:

 /*  */

  var uid$2 = 0

  /**
   * A watcher parses an expression, collects dependencies,
   * and fires callback when the expression value changes.
   * This is used for both the $watch() api and directives.
   */
  var Watcher = function Watcher(vm, expOrFn, cb, options, isRenderWatcher) {
    this.vm = vm
		// 在 mount component 实例化时 isRenderWatcher 为 true
		// 这时候挂载 _watcher 到 mounted component 实例上
    if (isRenderWatcher) {
      vm._watcher = this
    }
		// 在 initState 时候初始化了 `vm._watchers = []`
    vm._watchers.push(this)
    // options
    if (options) {
      this.deep = !!options.deep // watch deep
      this.user = !!options.user // from user defintion like $watch or ComponentOptions.watch
      this.lazy = !!options.lazy // lazy evaluate getter to get lazy value
      this.sync = !!options.sync
      this.before = options.before
    } else {
      this.deep = this.user = this.lazy = this.sync = false
    }
    this.cb = cb
    this.id = ++uid$2 // 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 = expOrFn.toString()
    // parse expression for getter
    if (typeof expOrFn === 'function') {
      this.getter = expOrFn
    } else {
      this.getter = parsePath(expOrFn)
      if (!this.getter) {
        this.getter = noop
        warn(
          'Failed watching path: "' +
            expOrFn +
            '" ' +
            'Watcher only accepts simple dot-delimited paths. ' +
            'For full control, use a function instead.',
          vm
        )
      }
    }

		// 如果是 lazy 的话,先不执行 get() 获取结果,
		// 在 ComponentOptions.computed 的声明中
		// 它的 get() 总是 lazy 得到的
    this.value = this.lazy ? undefined : this.get()
  }

  /**
   * Evaluate the getter, and re-collect dependencies.
   */
  Watcher.prototype.get = function get() {
		// 执行 get 时候, Dep.target 就是当前 Watcher
		// 此时所有的 getter/setter 都会该 Watcher 被订阅
		// dep.depend() => this.addDep(dep)
    pushTarget(this) // {1}
    var value
    var vm = this.vm
    try {
			// IMPORTANT: 执行 getter,传入当前上下文和实例 vm,
			// 此时,如果 getter 中访问了任何 reactive object 的属性
			// 则会通过上面的 {1} 赋值 Dep.target 注册依赖
			// 将当前 watcher 作为 subscribe 放进每个属性 dep 中
			// 而 watcher 同时也维护了对各个依赖属性的 deps 数组
			// 所以是 Observer 在 observe 时创建了 Dep
			// 而 watcher 在计算/获取值时赋值了 Dep.target
			// 因此此时凡是被访问到的对象的 getter 都会被 watcher 收集作为依赖
			// 而 dep 同时也会把 watcher 添加到 subs 中作为 subscribes
			// 当下次有更新时候,watcher 通知所有的 deps notify subs 更新即可
      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.
   */
  Watcher.prototype.addDep = function addDep(dep) {
    var id = dep.id
		// 在 watcher 实例化之后如果 newDepIds 和 oldDepId 中都没有该 dep
		// 则将当前 Watcher 实例添加在 Dep 的订阅中,dep 是所有的属性的 Observe
    if (!this.newDepIds.has(id)) {
      this.newDepIds.add(id)
      this.newDeps.push(dep)
      if (!this.depIds.has(id)) {
				// dep.subs 
        dep.addSub(this)
      }
    }
  }

  /**
   * Clean up for dependency collection.
   */
  Watcher.prototype.cleanupDeps = function cleanupDeps() {
    var i = this.deps.length
    while (i--) {
      var dep = this.deps[i]
			// TODO:newDepIds 是什么?为什么要有 newDepIds?
			// 从逻辑上看是从 dep 中删除 newDepIds 的 Sub
      if (!this.newDepIds.has(dep.id)) {
        dep.removeSub(this)
      }
    }

		// 这里做的是将 newDeps/newDepIds 赋值为 deps/depIds
		// 将旧的 deps/depIds 清空
    var 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
  }

  /**
   * Subscriber interface.
   * Will be called when a dependency changes.
   */
  Watcher.prototype.update = function update() {
    /* istanbul ignore else */
		// TODO: 如果是 lazy 则更新 dirty 为 true,所以 dirty 后续会作何处理?
    if (this.lazy) {
      this.dirty = true
    } else if (this.sync) {
			// TODO:如果 sync 则直接运行,那 sync 又意味着什么?
      this.run()
    } else {
			// 否则的话,丢进 queue 然后 flushCallbacks ?
      queueWatcher(this)
    }
  }

  /**
   * Scheduler job interface.
   * Will be called by the scheduler.
   */
  Watcher.prototype.run = function run() {
    if (this.active) {
			// 重新执行 get 方法时,如果有 reactive 属性被访问
			// 会被添加到当前 watcher.addDep(dep) 中记录(该 watcher 所依赖的 reactive 的 dep 实例)
			// 而经过 newDepIds 和 depIds 的比对后,
			// watcher 实例被添加到 dep.subs 中用于后续执行 setter 触发更新
			// 一旦进入 setter 则会 dep.notify 通知所有的 watcher.update()
      var 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
        var 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)
        }
      }
    }
  }

  /**
   * Evaluate the value of the watcher.
   * This only gets called for lazy watchers.
   */
  Watcher.prototype.evaluate = function evaluate() {
    this.value = this.get()
    this.dirty = false
  }

  /**
   * Depend on all deps collected by this watcher.
   */
  Watcher.prototype.depend = function depend() {
    var i = this.deps.length
    while (i--) {
      this.deps[i].depend()
    }
  }

  /**
   * Remove self from all dependencies' subscriber list.
   */
  Watcher.prototype.teardown = function 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)
      }
      var i = this.deps.length
      while (i--) {
        this.deps[i].removeSub(this)
      }
      this.active = false
    }
  }

	/**
   * Parse simple path.
   */
  var bailRE = new RegExp('[^' + unicodeRegExp.source + '.$_\\d]')
  function parsePath(path) {
    if (bailRE.test(path)) {
      return
    }
    var segments = path.split('.')
    return function(obj) {
      for (var i = 0; i < segments.length; i++) {
        if (!obj) {
          return
        }
        obj = obj[segments[i]]
      }
      return obj
    }
  }

	/*  */

  var seenObjects = new _Set()

  /**
   * Recursively traverse an object to evoke all converted
   * getters, so that every nested property inside the object
   * is collected as a "deep" dependency.
   * 递归遍历对象以调用所有转换的getter,这样对象中的每个嵌套属性都作为“深层”依赖项收集。
   */
  function traverse(val) {
		// TODO:我不明白这里这个 _traverse 的实际意义是什么?
		// 而且哪儿在收集?我并没有看到收集?
    _traverse(val, seenObjects)
    seenObjects.clear()
  }

  function _traverse(val, seen) {
    var i, keys
    var isA = Array.isArray(val)
    if (
      (!isA && !isObject(val)) ||
      Object.isFrozen(val) ||
      val instanceof VNode
    ) {
      return
    }
    if (val.__ob__) {
      var depId = val.__ob__.dep.id
      if (seen.has(depId)) {
        return
      }
      seen.add(depId)
    }
    if (isA) {
      i = val.length
      while (i--) {
        _traverse(val[i], seen)
      }
    } else {
      keys = Object.keys(val)
      i = keys.length
      while (i--) {
        _traverse(val[keys[i]], seen)
      }
    }
  }

在处理 AsyncComponent 时候的代码:

	function resolveAsyncComponent(factory, baseCtor) {
    // 如果有错则直接返回 errorComp
    if (isTrue(factory.error) && isDef(factory.errorComp)) {
      return factory.errorComp
    }

    // 如果已经 resolved 则直接返回 resolved
    if (isDef(factory.resolved)) {
      return factory.resolved
    }

    // 当前 owner 即正在渲染的 vm 实例
    // 我的理解是这一步在为调用了 AsyncComponent 收集依赖关系
    var owner = currentRenderingInstance
    if (
      owner &&
      isDef(factory.owners) &&
      factory.owners.indexOf(owner) === -1
    ) {
      // already pending
      factory.owners.push(owner)
    }

		// 如果 loading 且 loadingComp 已经初始化好了则渲染 loading 组件
    if (isTrue(factory.loading) && isDef(factory.loadingComp)) {
      return factory.loadingComp
    }

		// 如果存在 owner 且 factory.owners 之前还未初始化过则进入初始化逻辑
    if (owner && !isDef(factory.owners)) {
      var owners = (factory.owners = [owner])
      var sync = true
      var timerLoading = null
      var timerTimeout = null

      owner.$on('hook:destroyed', function() {
        return remove(owners, owner)
      })

			// 强制更新并渲染
      var forceRender = function(renderCompleted) {
        for (var i = 0, l = owners.length; i < l; i++) {
          owners[i].$forceUpdate()
        }

				// 渲染完成后清空内部状态
        if (renderCompleted) {
          owners.length = 0
          if (timerLoading !== null) {
            clearTimeout(timerLoading)
            timerLoading = null
          }
          if (timerTimeout !== null) {
            clearTimeout(timerTimeout)
            timerTimeout = null
          }
        }
      }

			// resolve 函数
      var resolve = once(function(res) {
        // cache resolved
        factory.resolved = ensureCtor(res, baseCtor)
        // invoke callbacks only if this is not a synchronous resolve
        // (async resolves are shimmed as synchronous during SSR)
        if (!sync) {
          forceRender(true)
        } else {
          owners.length = 0
        }
      })

      var reject = once(function(reason) {
        warn(
          'Failed to resolve async component: ' +
            String(factory) +
            (reason ? '\nReason: ' + reason : '')
        )
				// 出现错误后渲染错误组件
        if (isDef(factory.errorComp)) {
          factory.error = true
          forceRender(true)
        }
      })

			// 执行 factory 并传入 resolve, reject
      var res = factory(resolve, reject)

			// 如果返回值是个对象
      if (isObject(res)) {
				// 如果返回值是个对象且是个 promise
        if (isPromise(res)) {
          // () => Promise
          if (isUndef(factory.resolved)) {
            res.then(resolve, reject)
          }
        } else if (isPromise(res.component)) {
          res.component.then(resolve, reject)

          if (isDef(res.error)) {
            factory.errorComp = ensureCtor(res.error, baseCtor)
          }

          if (isDef(res.loading)) {
            factory.loadingComp = ensureCtor(res.loading, baseCtor)
            if (res.delay === 0) {
              factory.loading = true
            } else {
              timerLoading = setTimeout(function() {
                timerLoading = null
                if (isUndef(factory.resolved) && isUndef(factory.error)) {
                  factory.loading = true
                  forceRender(false)
                }
              }, res.delay || 200)
            }
          }

          if (isDef(res.timeout)) {
            timerTimeout = setTimeout(function() {
              timerTimeout = null
              if (isUndef(factory.resolved)) {
                reject('timeout (' + res.timeout + 'ms)')
              }
            }, res.timeout)
          }
        }
      }

      sync = false
      // return in case resolved synchronously
      return factory.loading ? factory.loadingComp : factory.resolved
    }
  }

在处理 EventEmitter 时候的代码,其实就是 Pub-Sub 的实现:

	// 挂载事件属性
	function initEvents(vm) {
		// subs pool
    vm._events = Object.create(null)
    vm._hasHookEvent = false
    // init parent attached events
		// 父组件所有的 on listener
    var listeners = vm.$options._parentListeners
    if (listeners) {
			// 见上面的描述,上面的源码解读有解释过函数内部的代码
      updateComponentListeners(vm, listeners)
    }
  }

	// 给 Vue.prototype 混入 EventEmitter 方法
	function eventsMixin(Vue) {
    var hookRE = /^hook:/
    Vue.prototype.$on = function(event, fn) {
      var vm = this
      if (Array.isArray(event)) {
        for (var i = 0, l = event.length; i < l; i++) {
          vm.$on(event[i], fn)
        }
      } else {
        ;(vm._events[event] || (vm._events[event] = [])).push(fn)
        // optimize hook:event cost by using a boolean flag marked at registration
        // instead of a hash lookup
        if (hookRE.test(event)) {
          vm._hasHookEvent = true
        }
      }
      return vm
    }

    Vue.prototype.$once = function(event, fn) {
      var vm = this
      function on() {
        vm.$off(event, on)
        fn.apply(vm, arguments)
      }
      on.fn = fn
      vm.$on(event, on)
      return vm
    }

    Vue.prototype.$off = function(event, fn) {
      var vm = this
      // all
      if (!arguments.length) {
        vm._events = Object.create(null)
        return vm
      }
      // array of events
      if (Array.isArray(event)) {
        for (var i$1 = 0, l = event.length; i$1 < l; i$1++) {
          vm.$off(event[i$1], fn)
        }
        return vm
      }
      // specific event
      var cbs = vm._events[event]
      if (!cbs) {
        return vm
      }
      if (!fn) {
        vm._events[event] = null
        return vm
      }
      // specific handler
      var cb
      var i = cbs.length
      while (i--) {
        cb = cbs[i]
        if (cb === fn || cb.fn === fn) {
          cbs.splice(i, 1)
          break
        }
      }
      return vm
    }

    Vue.prototype.$emit = function(event) {
      var vm = this
      {
        var lowerCaseEvent = event.toLowerCase()
        if (lowerCaseEvent !== event && vm._events[lowerCaseEvent]) {
          tip(
            'Event "' +
              lowerCaseEvent +
              '" is emitted in component ' +
              formatComponentName(vm) +
              ' but the handler is registered for "' +
              event +
              '". ' +
              'Note that HTML attributes are case-insensitive and you cannot use ' +
              'v-on to listen to camelCase events when using in-DOM templates. ' +
              'You should probably use "' +
              hyphenate(event) +
              '" instead of "' +
              event +
              '".'
          )
        }
      }
      var cbs = vm._events[event]
      if (cbs) {
        cbs = cbs.length > 1 ? toArray(cbs) : cbs
        var args = toArray(arguments, 1)
        var info = 'event handler for "' + event + '"'
        for (var i = 0, l = cbs.length; i < l; i++) {
          invokeWithErrorHandling(cbs[i], vm, args, vm, info)
        }
      }
      return vm
    }
  }

再看看 LifeCycle 的实现:

	// 挂载 LifeCycle	
	function initLifecycle(vm) {
    var options = vm.$options

    // locate first non-abstract parent
    var parent = options.parent
    if (parent && !options.abstract) {
			// 找到非抽象组件的 parent vm
      while (parent.$options.abstract && parent.$parent) {
        parent = parent.$parent
      }
      parent.$children.push(vm)
    }

    vm.$parent = parent
    vm.$root = parent ? parent.$root : vm

    vm.$children = []
    vm.$refs = {}

    vm._watcher = null
    vm._inactive = null
    vm._directInactive = false
    vm._isMounted = false
    vm._isDestroyed = false
    vm._isBeingDestroyed = false
  }

  var activeInstance = null

	// 这个函数是用闭包保存起 prevActiveInstance
	// 然后重置 activeInstance 为当前 vm,
  // 最后返回重置 activeInstance 回 prevActiveInstance 的函数
  function setActiveInstance(vm) {
    var prevActiveInstance = activeInstance
    activeInstance = vm
    return function() {
      activeInstance = prevActiveInstance
    }
  }

	// 给 Vue.prototype 混入 LifeCycle 方法
	function lifecycleMixin(Vue) {
		// TODO: 什么时候调用了 this._update? 应该是在 init/patch 组件时
		// 下方给除了 _update 函数的核心母的,就是 init/update 时
		// 都执行了 vm.__patch__ 函数,并调用重置函数 restoreActiveInstance
    Vue.prototype._update = function(vnode, hydrating) {
      var vm = this
      var prevEl = vm.$el
      var prevVnode = vm._vnode
      var restoreActiveInstance = setActiveInstance(vm)
      vm._vnode = vnode
      // Vue.prototype.__patch__ is injected in entry points
      // based on the rendering backend used.
      if (!prevVnode) {
        // initial render
        vm.$el = vm.__patch__(vm.$el, vnode, hydrating, false /* removeOnly */)
      } else {
        // updates
        vm.$el = vm.__patch__(prevVnode, vnode)
      }
      restoreActiveInstance()
      // update __vue__ reference
      if (prevEl) {
				// 清除 prevEl.__vue__ 引用,释放 __vue__ 内存
				// 那么 prevEl 内,无需释放了,因为上面 __patch__ 时重新赋值了
				// 则在函数结束后会被清空
        prevEl.__vue__ = null
      }
      if (vm.$el) {
				// 重新给当前 $el.__vue__ 写入 vm 实例
        vm.$el.__vue__ = vm
      }
      // if parent is an HOC, update its $el as well
			// TODO: how to judge parent is an HOC? `vm.$vnode === vm.$parent._vnode`???
			// 当前 $vnode,在之前的代码中是 vm.$vnode = parentVnode
      if (vm.$vnode && vm.$parent && vm.$vnode === vm.$parent._vnode) {
        vm.$parent.$el = vm.$el
      }
      // updated hook is called by the scheduler to ensure that children are
      // updated in a parent's updated hook.
    }

    Vue.prototype.$forceUpdate = function() {
      var vm = this
      if (vm._watcher) {
				// 原来 $forceUpdate 就是将所有的 _watcher 给 update 一遍
				// 然后这些 update 内部一定有跟 view 绑定的 getter/setter 以更新对应视图
        vm._watcher.update()
      }
    }

	// 把 callHook 复制进来看
	function callHook(vm, hook) {
    // #7573 disable dep collection when invoking lifecycle hooks
    pushTarget()
    var handlers = vm.$options[hook]
    var info = hook + ' hook'
    if (handlers) {
      for (var i = 0, j = handlers.length; i < j; i++) {
        invokeWithErrorHandling(handlers[i], vm, null, vm, info)
      }
    }
    if (vm._hasHookEvent) {
      vm.$emit('hook:' + hook)
    }
    popTarget()
  }

    Vue.prototype.$destroy = function() {
      var vm = this
			// 也就是说 $destroy 可能会被多次调用
      if (vm._isBeingDestroyed) {
        return
      }
      callHook(vm, 'beforeDestroy')
      vm._isBeingDestroyed = true
      // remove self from parent
      var parent = vm.$parent
			// 从 parent 中移除当前 vm,释放内存
      if (parent && !parent._isBeingDestroyed && !vm.$options.abstract) {
        remove(parent.$children, vm)
      }
      // teardown watchers
      if (vm._watcher) {
        vm._watcher.teardown()
      }
      var i = vm._watchers.length
      while (i--) {
        vm._watchers[i].teardown()
      }
      // remove reference from data ob
      // frozen object may not have observer.
      if (vm._data.__ob__) {
        vm._data.__ob__.vmCount--
      }
      // call the last hook...
      vm._isDestroyed = true
      // invoke destroy hooks on current rendered tree
      vm.__patch__(vm._vnode, null)
      // fire destroyed hook
      callHook(vm, 'destroyed')
      // turn off all instance listeners.
      vm.$off()
      // remove __vue__ reference
      if (vm.$el) {
        vm.$el.__vue__ = null
      }
      // release circular reference (#6759)
      if (vm.$vnode) {
        vm.$vnode.parent = null
      }
    }
  }

	function mountComponent(vm, el, hydrating) {
    vm.$el = el
    if (!vm.$options.render) {
      vm.$options.render = createEmptyVNode
      {
        /* istanbul ignore if */
        if (
          (vm.$options.template && vm.$options.template.charAt(0) !== '#') ||
          vm.$options.el ||
          el
        ) {
          warn(
            'You are using the runtime-only build of Vue where the template ' +
              'compiler is not available. Either pre-compile the templates into ' +
              'render functions, or use the compiler-included build.',
            vm
          )
        } else {
          warn(
            'Failed to mount component: template or render function not defined.',
            vm
          )
        }
      }
    }
    callHook(vm, 'beforeMount')

    var updateComponent
    /* istanbul ignore if */
    if (config.performance && mark) {
      updateComponent = function() {
        var name = vm._name
        var id = vm._uid
        var startTag = 'vue-perf-start:' + id
        var endTag = 'vue-perf-end:' + id

        mark(startTag)
        var vnode = vm._render()
        mark(endTag)
        measure('vue ' + name + ' render', startTag, endTag)

        mark(startTag)
        vm._update(vnode, hydrating)
        mark(endTag)
        measure('vue ' + name + ' patch', startTag, endTag)
      }
    } else {
      updateComponent = function() {
				// mounted component to generates component vnode instance
        vm._update(vm._render(), hydrating)
      }
    }

    // we set this to vm._watcher inside the watcher's constructor
    // since the watcher's initial patch may call $forceUpdate (e.g. inside child
    // component's mounted hook), which relies on vm._watcher being already defined
    new Watcher(
      vm,
      updateComponent,
      noop,
      {
        before: function before() {
          if (vm._isMounted && !vm._isDestroyed) {
            callHook(vm, 'beforeUpdate')
          }
        }
      },
      true /* isRenderWatcher */
    )
    hydrating = false

    // manually mounted instance, call mounted on self
    // mounted is called for render-created child components in its inserted hook
    if (vm.$vnode == null) {
      vm._isMounted = true
      callHook(vm, 'mounted')
    }
    return vm
  }

接下来的核心就是看看 Vue 中的 Component 的实现了,但据以往关于 Virtual-DOM 的经验来看,就是渲染函数 render + 虚拟节点 VNode + Diff 算法 path。并围绕这三个部分编写生命周期函数,complie & optimize,都是写老生常谈的话题了,略过吧。

??