个人博客地址
Vue 内部,有一段这样的代码:

import { initMixin } from './init'import { stateMixin } from './state'import { renderMixin } from './render'import { eventsMixin } from './events'import { lifecycleMixin } from './lifecycle'import { warn } from '../util/index'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')  }  this._init(options)}initMixin(Vue)stateMixin(Vue)eventsMixin(Vue)lifecycleMixin(Vue)renderMixin(Vue)

上面5个函数的作用是在Vue的原型上面挂载方法。

  • initMixin 函数

    export function initMixin (Vue: Class<Component>) {  Vue.prototype._init = function (options?: Object) {    const vm: Component = this    // a uid    vm._uid = uid++    let startTag, endTag    /* istanbul ignore if */    if (process.env.NODE_ENV !== 'production' && config.performance && mark) {      startTag = `vue-perf-start:${vm._uid}`      endTag = `vue-perf-end:${vm._uid}`      mark(startTag)    }    // a flag to avoid this being observed    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 {      vm.$options = mergeOptions(        resolveConstructorOptions(vm.constructor),        options || {},        vm      )    }    /* istanbul ignore else */    if (process.env.NODE_ENV !== 'production') {      initProxy(vm)    } else {      vm._renderProxy = vm    }    // expose real self    // 初始化操作    vm._self = vm    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)    }  }}

    可以看到在 initMixin 方法中,实现了一系列的初始化操作,包括生命周期流程以及响应式系统流程的启动。

  • stateMixin 函数

    export function stateMixin (Vue: Class<Component>) {  // flow somehow has problems with directly declared definition object  // when using Object.defineProperty, so we have to procedurally build up  // the object here.  const dataDef = {}  dataDef.get = function () { return this._data }  const propsDef = {}  propsDef.get = function () { return this._props }  if (process.env.NODE_ENV !== 'production') {    dataDef.set = function () {      warn(        'Avoid replacing instance root $data. ' +        'Use nested data properties instead.',        this      )    }    propsDef.set = function () {      warn(`$props is readonly.`, this)    }  }  Object.defineProperty(Vue.prototype, '$data', dataDef)  Object.defineProperty(Vue.prototype, '$props', propsDef)  Vue.prototype.$set = set  Vue.prototype.$delete = del  Vue.prototype.$watch = function (    expOrFn: string | Function,    cb: any,    options?: Object  ): Function {    const vm: Component = this    if (isPlainObject(cb)) {      return createWatcher(vm, expOrFn, cb, options)    }    options = options || {}    options.user = true    const 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()    }  }}

    stateMixin 被调用时,往Vue的原型上了挂载了三个方法:$delete$set$watch

  • eventsMixin 函数

    export function eventsMixin (Vue: Class<Component>) {  const hookRE = /^hook:/  // $on的实现:在注册时把回调函数收集起来,在触发时将收集的事件依次调用  Vue.prototype.$on = function (event: string | Array<string>, fn: Function): Component {    const vm: Component = this          // 当event为数组时,遍历event将其中的每一项都调用$on    // 当event为字符串时,向事件列表中添加回调    // vm._enevts是专门用来存储事件,在initMixin中生成:vm._events = Object.create(null)    if (Array.isArray(event)) {      for (let 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: string, fn: Function): Component {    const vm: Component = this    // 当第一次触发自定义事件时,会移除这个事件监听器,然后手动运行fn函数    function on () {      vm.$off(event, on)      fn.apply(vm, arguments)    }    on.fn = fn    vm.$on(event, on)                return vm  }  Vue.prototype.$off = function (event?: string | Array<string>, fn?: Function): Component {    const vm: Component = this    // all    // 当参数为空时,直接清空vm._events,移除所有事件监听器    if (!arguments.length) {      vm._events = Object.create(null)      return vm    }    // array of events    // 当event为数组时,遍历event每一项都调用$off    if (Array.isArray(event)) {      for (let i = 0, l = event.length; i < l; i++) {        vm.$off(event[i], fn)      }      return vm    }    // specific event    const cbs = vm._events[event]    // 如果事件列表里面没有这个方法,直接返回    if (!cbs) {      return vm    }    // 如果回调函数不存在,移除该事件的所有监听器    if (!fn) {      vm._events[event] = null      return vm    }    // specific handler    // 从vm._events中删除这个事件监听器    let cb    let i = cbs.length    while (i--) {      cb = cbs[i]      if (cb === fn || cb.fn === fn) {        cbs.splice(i, 1)        break      }    }    return vm  }  // $emit的实现:使用事件名event从vm._events中取出事件监听器的回调函数列表  // 依次执行列表中的回调函数并且把参数传入监听器回调函数  Vue.prototype.$emit = function (event: string): Component {    const vm: Component = this    if (process.env.NODE_ENV !== 'production') {      const 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}".`        )      }    }    let cbs = vm._events[event]    if (cbs) {      cbs = cbs.length > 1 ? toArray(cbs) : cbs      const args = toArray(arguments, 1)      const info = `event handler for "${event}"`      for (let i = 0, l = cbs.length; i < l; i++) {        invokeWithErrorHandling(cbs[i], vm, args, vm, info)      }    }    return vm  }}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)    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}

    eventsMixin 函数被调用时,往 Vue 的原型上挂载了4个方法:$on$once$off$emit

  • lifecycleMixin 函数

    export function lifecycleMixin (Vue: Class<Component>) {  Vue.prototype._update = function (vnode: VNode, hydrating?: boolean) {    const vm: Component = this    const prevEl = vm.$el    const prevVnode = vm._vnode    const 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__ = null    }    if (vm.$el) {      vm.$el.__vue__ = vm    }    // if parent is an HOC, update its $el as well    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.  }  // vm._watcher就是Vue.js实例的watcher,手动执行watcher的update方法  // 然后组件内部就会重新生成vnode,和旧的vnode进行对比,更新视图  Vue.prototype.$forceUpdate = function () {    const vm: Component = this    if (vm._watcher) {      vm._watcher.update()    }  }  //   Vue.prototype.$destroy = function () {    const vm: Component = this    // 如果已经在销毁实例,则直接返回    if (vm._isBeingDestroyed) {      return    }    // 调用钩子函数:beforeDestory    callHook(vm, 'beforeDestroy')    vm._isBeingDestroyed = true    // remove self from parent    // 删除自己与父级之间的链连接    const parent = vm.$parent    // 如果有父级,并且父级没有被销毁并且也不是抽象组件    if (parent && !parent._isBeingDestroyed && !vm.$options.abstract) {      remove(parent.$children, vm)    }    // teardown watchers    // 从watcher监听的所有状态的依赖列表中移除watcher    if (vm._watcher) {      vm._watcher.teardown()    }    // 在Watcher类中有这样一行代码: vm._watchers.push(this)    // 移除所有用户通过$watch创建的watcher实例    let 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    // 在vnode树上触发destory钩子函数解绑指令    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    }  }}

    lifecycleMixin 被调用时,往 Vue 的原型上挂载了三个方法:_updata$forceUpdate$destory

  • renderMixin 函数

    export function renderMixin (Vue: Class<Component>) {  // install runtime convenience helpers  installRenderHelpers(Vue.prototype)  Vue.prototype.$nextTick = function (fn: Function) {    return nextTick(fn, this)  }  Vue.prototype._render = function (): VNode {    const vm: Component = this    const { render, _parentVnode } = vm.$options    if (_parentVnode) {      vm.$scopedSlots = normalizeScopedSlots(        _parentVnode.data.scopedSlots,        vm.$slots,        vm.$scopedSlots      )    }    // set parent vnode. this allows render functions to have access    // to the data on the placeholder node.    vm.$vnode = _parentVnode    // render self    let vnode    try {      // There's no need to maintain a stack because all render fns are called      // separately from one another. Nested component's render fns are called      // when parent component is patched.      currentRenderingInstance = vm      vnode = render.call(vm._renderProxy, vm.$createElement)    } catch (e) {      handleError(e, vm, `render`)      // return error render result,      // or previous vnode to prevent render error causing blank component      /* istanbul ignore else */      if (process.env.NODE_ENV !== 'production' && vm.$options.renderError) {        try {          vnode = vm.$options.renderError.call(vm._renderProxy, vm.$createElement, e)        } catch (e) {          handleError(e, vm, `renderError`)          vnode = vm._vnode        }      } else {        vnode = vm._vnode      }    } finally {      currentRenderingInstance = null    }    // if the returned array contains only a single node, allow it    if (Array.isArray(vnode) && vnode.length === 1) {      vnode = vnode[0]    }    // return empty vnode in case the render function errored out    if (!(vnode instanceof VNode)) {      if (process.env.NODE_ENV !== 'production' && Array.isArray(vnode)) {        warn(          'Multiple root nodes returned from render function. Render function ' +          'should return a single root node.',          vm        )      }      vnode = createEmptyVNode()    }    // set parent    vnode.parent = _parentVnode    return vnode  }}// 简化版的nextTick// 用来存放回调函数事件let callbacks = []// 表示是否已经添加到微任务列表中let pending = false// 遍历callbacks,清空callbacks,依次调用回调函数function flushCallbacks () {    penging = false    const copies = callbacks.slice(0)    callbacks.length = 0    for (let i = 0; i < copies.length; i++) {        capies[i]()    }}// 把事件添加到微任务列表中去let microTimerFunclet p = Promise.resolve()microTimerFunc = () => {    p.then(flushCallbacks)}function nextTick (cb?: Function, ctx?: Object) {  // 一进来先向callback中添加回调事件  callbacks.push(() => {      if (cb) {        cb.call(ctx)      }  })  // 如果pending为false,则表示是第一次执行nextTick,将其添加到微任务中  // 如果pending为true,表示之前微任务列表中已经添加了这个方法,直接退出  if (!pending) {      pending = true      microTimerFunc()  }}

    renderMixin 被调用时,在 Vue 的原型上挂载了两个方法:$nextTick_render