Vue原型初始化——vue2源码探究(6)

129 阅读2分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第20天,点击查看活动详情

我们查看一下Vue实例化的源码文件:

// 源码文件 src\core\instance\index.ts
function Vue(options) {
  if (__DEV__ && !(this instanceof Vue)) {
    warn('Vue is a constructor and should be called with the `new` keyword')
  }
  this._init(options)
}

//@ts-expect-error Vue has function type
initMixin(Vue)
//@ts-expect-error Vue has function type
stateMixin(Vue)
//@ts-expect-error Vue has function type
eventsMixin(Vue)
//@ts-expect-error Vue has function type
lifecycleMixin(Vue)
//@ts-expect-error Vue has function type
renderMixin(Vue)

export default Vue as unknown as GlobalAPI

可以看到实例化过程中就是执行了_init方法,这个方法在initMixin中声明在了Vue的原型对象上,而其它的几个方法类似,都是将给Vue的原型对象上声明了各种各样的方法并进行初始化:

  • initMixin声明了实例的初始化方法(这篇不展开讲,后面单独说)
  • stateMixin声明了响应性对象的相关方法
  • eventsMixin声明了事件处理的相关方法
  • lifecycleMixin声明了生命周期的相关方法
  • renderMixin声明了渲染相关方法

stateMixin

stateMixin中主要是对响应性对象data,props的相关方法进行了处理,首先将二者分别挂载在$data,$props上:

// 源码文件:src\core\instance\state.ts
const dataDef: any = {}
dataDef.get = function () {
return this._data
}
const propsDef: any = {}
propsDef.get = function () {
return this._props
}
if (__DEV__) {
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)

声明$set,$props,$watch方法:

// 源码文件:src\core\instance\state.ts
Object.defineProperty(Vue.prototype, '$data', dataDef)
Object.defineProperty(Vue.prototype, '$props', propsDef)

Vue.prototype.$set = set // set 和 del方法在observer/index中,具体原理后面API部分再讲
Vue.prototype.$delete = del

Vue.prototype.$watch = function (
  expOrFn: string | (() => any),
  cb: any,
  options?: Record<string, any>
): 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) {
    const info = `callback for immediate watcher "${watcher.expression}"`
    pushTarget()
    invokeWithErrorHandling(cb, vm, [watcher.value], vm, info)
    popTarget()
  }
  return function unwatchFn() {
    watcher.teardown()
  }
}

eventsMixin

eventsMixin中声明了事件相关的方法$on,$once,$off,$emit(具体原理和用法在后面实例方法篇单独说,篇幅有限这里就不贴全部代码了):

// 源码文件:src\core\instance\events.ts
export function eventsMixin(Vue: typeof Component) {
  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 {
  // ...
  }
}

lifecycleMixin

lifecycleMixin中声明了生命周期的相关方法_update,$forceUpdate,$destroy:

// 源码文件:src\core\instance\lifecycle.ts
export function lifecycleMixin(Vue: typeof Component) {
  Vue.prototype._update = function (vnode: VNode, hydrating?: boolean) {}
  Vue.prototype.$forceUpdate = function () {}
  Vue.prototype.$destroy = function () {}
}

renderMixin

renderMixin中声明了模板渲染的相关方法$nextTick_render,并且挂载了渲染工具函数installRenderHelpers,提供了运行时Vue(手写render)的支持:

// 源码文件:src\core\instance\render.ts
export function lifecycleMixin(Vue: typeof Component) {
  // install runtime convenience helpers
  installRenderHelpers(Vue.prototype)
  
  Vue.prototype.$nextTick = function (fn: (...args: any[]) => any) {}
  Vue.prototype._render = function (): VNode {}
}

Vue原型初始化完毕之后,就可以开始使用了,用new Vue()进行实例化开始初始化Vue对象,进入Vue的生命周期,下一篇文章将详细解读一下这个过程。