Skip to content

new Vue的初始化过程做了什么?

1. Vue 构造函数

首先,我们根据入口文件,找到了 vue 构造函数,位于src/core/instance/index.js文件,内容如下:

js
function Vue(options) {
  // 非生产环境下,没有new Vue就是用。
  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); // new Vue时候会调用
}
// 下面每一个mixin,都是往Vue.prototype上添加一些方法
initMixin(Vue);
stateMixin(Vue);
eventsMixin(Vue);
lifecycleMixin(Vue);
renderMixin(Vue);

export default Vue; // 最终导出Vue,但是这里的Vue并非用户拿到的Vue,后面还会基于aop切片编程,对Vue进一步处理

可以看到,new Vue({})时候,会执行_init函数,并将用户传入的options,也就是包含datamethodscomputedwatch等属性方法的对象,那么问题来了_init()函数在哪儿定义的呢?答案就是initMixin(Vue),这里initMixin方法执行后, 会在Vue原型上挂上一个_inti方法。Vue.prototype._init

2. Vue.prototype._init(options)

initMixin()方法的位置在src/core/instance/init.js文件。内容如下

js
let uid = 0;

export function initMixin(Vue: Class<Component>) {
  Vue.prototype._init = function (options?: Object) {
    const vm: Component = this; // _init在Vue里面是this调用的,所以这里的this就是Vue实例
    // 当前Vue的唯一标志
    vm._uid = uid++;

    /****************不是生产环境下进行的性能监控 监控开始位置,可以忽略********************/
    let startTag, endTag;
    if (process.env.NODE_ENV !== "production" && config.performance && mark) {
      startTag = `vue-perf-start:${vm._uid}`;
      endTag = `vue-perf-end:${vm._uid}`;
      mark(startTag);
    }

    vm._isVue = true; // 一个标志,避免该对象被响应系统观测

    /********** 对 Vue 提供的 props、data、methods等选项进行合并处理 ************/
    if (options && options._isComponent) {
      // 优化内部组件实例化,因为动态选项合并速度非常慢,内部组件选项需要特殊处理。
      initInternalComponent(vm, options); // 其实就是将父组件挂到vm.$options上,减少查找次数,从而优化性能
    } else {
      vm.$options = mergeOptions(
        resolveConstructorOptions(vm.constructor),
        options || {},
        vm
      ); // 将子组件的options合并到vm.$options上
    }

    // 设置渲染函数的作用域代理,其目的是提供更好的提示信息(如:在模板内访问实例不存在的属性,则会在非生产环境下提供准确的报错信息)
    if (process.env.NODE_ENV !== "production") {
      initProxy(vm);
    } else {
      vm._renderProxy = vm;
    }

    // 暴露真实的实例本身
    vm._self = vm;

    /* ***********执行相关初始化程序及调用初期生命周期函数*********** */
    initLifecycle(vm); // 初始化生命周期
    initEvents(vm); // 初始化事件
    initRender(vm); // 初始化渲染
    callHook(vm, "beforeCreate"); // 调用 beforeCreated 钩子
    initInjections(vm); // resolve injections before data/props
    initState(vm); // 初始化 initProps、initMethods、initData、initComputed、initWatch
    initProvide(vm); // resolve provide after data/props
    callHook(vm, "created"); // 调用 created 钩子

    /****************不是生产环境下进行的性能监控 监控结束位置,可以忽略********************/
    if (process.env.NODE_ENV !== "production" && config.performance && mark) {
      vm._name = formatComponentName(vm, false);
      mark(endTag);
      measure(`vue ${vm._name} init`, startTag, endTag);
    }

    // 根据判断是否有el挂载点,挂载实例
    if (vm.$options.el) {
      vm.$mount(vm.$options.el);
    }
  };
}

总结Vue.prototype._init做的事情:

Vue.prototype._init方法可以总结出,new Vue 的时候,主要做了一下事情:

  1. mergeOptions方法合并选项 props、data、methods 等(包括组件的查找优化,递归合并子组件选项)都会合并到 vm.$options 上面
  2. 设置渲染函数的作用域代理,其目的是提供更好的提示信息
  3. 一些初始化工作和生命周期函数调用
    • 初始化生命周期
    • 初始化事件
    • 初始化渲染
    • 调用生命周期钩子函数-beforeCreate
    • 初始化Inject
    • 初始化 state,包括 props、methods、data、computed、watch
    • 初始化provide
    • 调用生命周期钩子函数-created
  4. el存在 new Vue 时传入的对象中,则调用实例上面的$mont方法,并传入el

下面来依次分析Vue.prototype._init内的每一个方法, 主要是这一部分

初始化内置组件initInternalComponent这部分暂时跳过

js
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 as any)
} else {
  vm.$options = mergeOptions(
    resolveConstructorOptions(vm.constructor as any),
    options || {},
    vm
  )
}


initLifecycle(vm); // 初始化生命周期
initEvents(vm); // 初始化事件
initRender(vm); // 初始化渲染
callHook(vm, "beforeCreate"); // 调用 beforeCreated 钩子
initInjections(vm); // resolve injections before data/props
initState(vm); // 初始化 initProps、initMethods、initData、initComputed、initWatch
initProvide(vm); // resolve provide after data/props
callHook(vm, "created"); // 调用 created 钩子