VUE源码学习第三篇--new Vue都干了啥(概述)

一、总体分析

当我们写下这段简单new Vue()代码,vue框架做了什么呢?

var vm = new Vue({
   el:"#app",
     data:{
          msg:'tttt'
   }
})

会调用src/core/instance/index.js的Vue构造方法

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)
}

     寥寥几句,承载了Vue的构造大业。判断当前的环境是不是生产环境,以及是不是采用new操作符创建的Vue对象,如果不是,则给出告警。接下来调用_init方法,是我们要重点分析的,其入参options就是我们定义的对象时传入的参数对象。

{
   el:"#app",
   data:{
          msg:'tttt'
   }
}

_init()的实现在src/core/instance/init.js中。

 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
    /**
     *第二部分,合并相关option
     */
    // merge options
    console.log("befor merge",vm.$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
      )
    }
    console.log("after merge",vm.$options);

     /**
     *第三部分,初始化相关功能
     */
    /* 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)
    }
     
   /**
     *第四部分,mount
     */
    if (vm.$options.el) {
      vm.$mount(vm.$options.el)
    }
  }

我们将_init过程划分四个部分分析。

二、分步分析

第一部分:设置属性,uid以及isVue的flag,没啥好说的。

第二部分:对options进行合并,vue会将相关的属性和方法都统一放到vm.$options中,为后续的调用做准备工作。vm.$option的属性来自两个方面,一个是Vue的构造函数(vm.constructor)预先定义的,一个是new Vue时传入的入参对象。以开篇的实例为例,合并完成后的options属性包括:

第三部分,初始化各类属性和事件,我们梳理成下面的导图

initProxy:设置vm._renderProxy。

initLifecycle:初始化一系列变量。

initEvents:存储父组件绑定当前子组件的事件,保存到vm._events。

initRende:定义vm._c和 vm.$createElement等方法。

callHook:调用生命周期的钩子相关方法,包括beforeCreate,created。

initInjections与initProvide:通过逐级查找,从父级provide中获取子级组件inject定义的属性值,并增加对该属性的监听。

initState:是对prop,method,data,computed,watch的初始化,增加对定义属性的监听。

第四部分:挂载。如果说前面几部分都是准备阶段,那么这部分是整个new Vue的核心部分,将template编译成render表达式,然后转化为大名鼎鼎的Vnode,最终渲染为真实的dom节点。同样我们给出流程图(后面我们将重点分析)。

三、总结

    Vue 的初始化逻辑写的非常清楚,把不同的功能逻辑拆成一些单独的函数执行,让主线逻辑一目了然。本篇对每个部分的作用做了大概的描述,如果仅做一般性的了解,可以跳过接下来的章节;如详细了解,请做好准备,下面一起享受每部分的源码。

上一篇:VUE源码学习第二篇--准备工作                              下一篇:VUE源码学习第四篇--new Vue都干了啥(options合并)

发布了33 篇原创文章 · 获赞 95 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/tcy83/article/details/86548070