initMixin流程

在上一节我们讲到了initGlobalAPI的整体流程,这一节,我们来介绍initMixin的整体流程。首先,我们把目光回到src/core/index.js文件中:

import Vue from './instance/index'
import { initGlobalAPI } from './global-api/index'
initGlobalAPI(Vue)

export default Vue

我们发现,它从别的模块中引入了大Vue,那么接下来我们的首要任务就是揭开Vue构造函数的神秘面纱。

在看src/core/instance/index.js代码之前,我们发现instance目录结构如下:

|-- instance
|   |-- render-helpers      # render渲染相关的工具函数目录
|   |-- events.js           # 事件处理相关
|   |-- init.js             # _init等方法相关
|   |-- inject.js           # inject和provide相关
|   |-- lifecycle.js        # 生命周期相关
|   |-- proxy.js            # 代理相关
|   |-- render.js           # 渲染相关
|   |-- state.js            # 数据状态相关
|   |-- index.js            # 入口文件

可以看到,目录结构文件有很多,而且包含的面也非常杂,但我们现在只需要对我们最关心的几个部分做介绍:

  • events.js:处理事件相关,例如:$on$off$emit以及$once等方法的实现。
  • init.js:此部分代码逻辑包含了Vue从创建实例到实例挂载阶段的所有主要逻辑。
  • lifecycle.js:生命周期相关,例如:$destroy$activated$deactivated
  • state.js:数据状态相关,例如:dataprops以及computed等。
  • render.js:渲染相关,其中最值得关注的是Vue.prototype._render渲染函数的定义。

在介绍了instance目录结构的及其各自的作用以后,我们再来看入口文件,其实入口文件这里才是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)

export default Vue

代码分析:

  • Vue构造函数其实就是一个普通的函数,我们只能通过new操作符进行访问,既new Vue()的形式,Vue函数内部也使用了instanceof操作符来判断实例的父类是否为Vue构造函数,不是的话则在开发环境下输出警告信息。
  • 除了声明Vue构造函数,这部分的代码也调用了几种mixin方法,其中每种mixin方法各司其职,处理不同的内容。

从以上代码中,我们能得到src/core/instance/index.js文件非常直观的代码逻辑流程图:

instance流程

接下来我们的首要任务是弄清楚_init()函数的代码逻辑以及initMixin的整体流程。我们从上面的代码发现,在构造函数内部会调用this._init()方法,如下:

// 实例化时,会调用this._init()方法。
new Vue({
  data: {
    msg: 'Hello, Vue.js'
  }
})

然后,我们在init.js中来看initMixin()方法是如何被定义的:

export function initMixin (Vue) {
  Vue.prototype._init = function (options) {
    // 省略代码
  }
}

我们可以发现,initMixin()方法的主要作用就是在Vue.prototype上定义一个_init()实例方法,接下来我们来看一下_init()函数的具体实现逻辑:

Vue.prototype._init = function (options) {
    const vm = this
    // 1. 合并配置
    if (options && options._isComponent) {
      initInternalComponent(vm, options)
    } else {
      vm.$options = mergeOptions(
        resolveConstructorOptions(vm.constructor),
        options || {},
        vm
      )
    }

    // 2.render代理
    if (process.env.NODE_ENV !== 'production') {
      initProxy(vm)
    } else {
      vm._renderProxy = vm
    }

    // 3.初始化生命周期、初始化事件中心、初始化inject,
    //   初始化state、初始化provide、调用生命周期
    vm._self = vm
    initLifecycle(vm)
    initEvents(vm)
    initRender(vm)
    callHook(vm, 'beforeCreate')
    initInjections(vm)
    initState(vm)
    initProvide(vm)
    callHook(vm, 'created')

    // 4.挂载
    if (vm.$options.el) {
      vm.$mount(vm.$options.el)
    }
  }

因为我们是要分析initMixin整体流程,对于其中某些方法的具体实现逻辑会在后续进行详细的说明,因此我们可以从以上代码得到initMixin的整体流程图。

最后更新时间:
贡献者: wangtunan