Vue源码之Vue初始化

225 阅读3分钟

最近打算将Vue源码再过一遍,同时给大家做一个Vue源码系列,将我分析源码的进度及思路给大家讲解,我会尽量做到让新手都能读懂,我分析的源码版本为6.11.3。

分析源码之前我先说一个概念,遇到函数定义时,如果看不懂就不要钻牛角尖,不要犹豫,直接跳过,等到调用的时候随着携带的参数再来分析。

vue入口

文件地址:src\core\instance\index.js

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)

export default Vue

文件里边定义了一个Vue方法,然后就是一些初始化函数,我们先看看initMixin(Vue)

文件地址:src\core\instance\init.js

export function initMixin (Vue: Class<Component>) {
  Vue.prototype._init = function (options?: Object) {...}
}

这里很简单,就是在Vue的原型上挂载了一个_init的方法,至于里边写了什么我们暂时不用看,等到调用的时候我们再来仔细看,现在只要知道有这个东西就行了,我们看下一个方法stateMixin(Vue)

文件地址:src\core\instance\state.js

export function stateMixin (Vue: Class<Component>) {
  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 {...}
}

这里也是在Vue的原型上挂载了一些属性和方法(data,data, props, set,set, delete, $watch)。 再来看下一个方法eventsMixin(Vue)。

文件地址:src\core\instance\events.js

export function eventsMixin (Vue: Class<Component>) {
  const hookRE = /^hook:/
  Vue.prototype.$on = function (event: string | Array<string>, fn: Function): Component {...}

  Vue.prototype.$once = function (event: string, fn: Function): Component {...}

  Vue.prototype.$off = function (event?: string | Array<string>, fn?: Function): Component {...}

  Vue.prototype.$emit = function (event: string): Component {...}
}

同样的,细节我们就仔细看了,等到调用的时候再来分析,从代码中看出,这里就是挂载了实例的4个事件方法(on,on, once, off,off, emit),再看下一个lifecycleMixin(Vue)

文件地址:src\core\instance\lifecycle.js

export function lifecycleMixin (Vue: Class<Component>) {
  Vue.prototype._update = function (vnode: VNode, hydrating?: boolean) {...}

  Vue.prototype.$forceUpdate = function () {...}

  Vue.prototype.$destroy = function () {...}
}

同样的,这里也只是挂载了3个方法,.update,._update, forceUpdate, $destroy,不多说,抬走下一个renderMixin(Vue)

文件地址:src\core\instance\render.js

export function renderMixin (Vue: Class<Component>) {
  // install runtime convenience helpers
  installRenderHelpers(Vue.prototype)

  Vue.prototype.$nextTick = function (fn: Function) {...}

  Vue.prototype._render = function (): VNode {...}
}

其他的都是差不多的,只是这里调用了另外一个函数installRenderHelpers(),在分析这个函数前,我们先看看Vue.prototype中都有一些什么。

> Vue.prototype
> {
  _update: function(){...},
  _init: function(){...},
  $destroy: function () {...},
  $emit: function () {...},
  $forceUpdate: function () {...},
  $off: function () {...},
  $on: function () {...},
  $once: function () {...},
  $set: function(){...},
  $delete: function(){...},
  $data: {...},
  $props: {...},
  constructor: function(){...},
  get $data: function(){...},
  set $data: function(){...},
  get $props: function(){...},
  set $props: function(){...}
}

我们可以看到,Vue.prototype中的属性和方法都是刚刚各个初始化函数挂载上去的。我们再来分析installRenderHelpers()

文件地址:src\core\instance\render-helpers\index.js

import { toNumber, toString, looseEqual, looseIndexOf } from 'shared/util'
import { createTextVNode, createEmptyVNode } from 'core/vdom/vnode'
import { renderList } from './render-list'
import { renderSlot } from './render-slot'
import { resolveFilter } from './resolve-filter'
import { checkKeyCodes } from './check-keycodes'
import { bindObjectProps } from './bind-object-props'
import { renderStatic, markOnce } from './render-static'
import { bindObjectListeners } from './bind-object-listeners'
import { resolveScopedSlots } from './resolve-scoped-slots'
import { bindDynamicKeys, prependModifier } from './bind-dynamic-keys'

export function installRenderHelpers (target: any) {
  target._o = markOnce
  target._n = toNumber
  target._s = toString
  target._l = renderList
  target._t = renderSlot
  target._q = looseEqual
  target._i = looseIndexOf
  target._m = renderStatic
  target._f = resolveFilter
  target._k = checkKeyCodes
  target._b = bindObjectProps
  target._v = createTextVNode
  target._e = createEmptyVNode
  target._u = resolveScopedSlots
  target._g = bindObjectListeners
  target._d = bindDynamicKeys
  target._p = prependModifier
}

我们看到这里也简单,就是挂载了一些渲染方法,这些方法是用于渲染和解析template模板的,这些等到我们以后再说。

至此,Vue的初始化操作差不多已经完成了七七八八,至于其他的,我们遇到了的时候再来分析。