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<functional|abstract|class|HOC> = 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 如何实现依赖收集?
    主要基于 Observer, Dep, Watcher 三个对象实现:

    1. Observer 观察者:在 Vue2 的 option API 模式中,使用 Observer(观察者,基于 ES5 的 Object.defineProperty API 实现)生成响应式对象(Reactive Object),拦截对象每个 key 的 getter/setter 植入 dep.depend() / dep.notify() 即 Sub/Pub。
    2. Dep 依赖管理: Observer 跟 Watcher 的 依赖关系 的 动态收集器。
    3. Watcher 订阅者: 在 Watcher.prototype.get()pushTarget(this) 指定 Dep.target 并调用 this.getter 订阅所有的 deps 依赖关系,并在 setter() -> dep.notify() 后执行所有 deps 中 watcher 的 update() 更新依赖。

    当访问响应式对象属性时,会进入 getter 劫持函数的逻辑。
    1. 如果存在有效的 Dep.target(即当前收集依赖的 watcher),
    2. 调用 watcher.addDep(dep) 收集当前属性的 dep 对象作为 watcher 的依赖,
    3. 同时执行 dep.addSub(this) 收集 watcher 作为 dep 的 subscribes 订阅者。
    当对响应式对象的属性赋值时,会进入 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 (即收集依赖的全局唯一的目标 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 的更新?
    VueRouter 源码记录 - 搞懂 router hooks 和 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<Component>) {
	// 写入 _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<Component>) {
  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<Component>): ?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<any>
} => {
  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<Function>, 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<VNode>,
  context: ?Component
): { [key: string]: Array<VNode> } {
  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,都是写老生常谈的话题了,略过吧。

🔚

posted @ 2021-07-29 21:34  月光宝盒造梦师  阅读(139)  评论(0编辑  收藏  举报