vue深入学习--new Vue()

120 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第1天,点击查看活动详情

在本篇文章中,作者会简单的介绍下new Vue()的流程,来帮助读者更好的理解vue背后的执行原理。

第一步

在vue的构造函数中,会调用了一个_init()初始化的方法,进行初始化的操作。

function Vue(options){
......
this._init(options)
}

第二步

在_init()初始化函数中,会进行一系列的初始化的操作,包括生命周期的回调。理解这个过程可以更好地理解,生命周期钩子是什么时候回调,我们需要在什么时间段使用生命周期钩子。 首先会处理

1.首先会检测组件的名字是否合格,限制组件的名字必须由普通字符和“-”组成的,且必须以字母开头。

2.判断是否是组件,如果是组件的话,会对组件的options进行处理,将层级比较深的属性放在$options中,减少了查找时的消耗。

if (options && options._isComponent) {
      initInternalComponent(vm, options)//是组件的话走这步,优化options
    } else { // 合并vue属性
      vm.$options = mergeOptions(
        resolveConstructorOptions(vm.constructor),
        options || {},
        vm
      )
    }

3.如果是根组件的话,那么就会合并options。在合并的过程中,会规范化,inject、directives,props等,因为这三项的写法有多种形式,所以我们需要将他们的格式统一进行合并。

inject:['foo'],inject:{bar:'foo'} ==> inject:{foo:{from: 'foo' }}
props:['size','number'],props:{height:{type:number}} ==>props: { size: { type: null },
myMessage: { type: null } },props: { height: { type: Number } }

4.混入合并

5.返回一个全新的options

第三步

在_init()初始化函数中,会进行一系列的初始化操作,如生命周期,事件等经常出现在vue官网中生命周期的那张图中。

// 初始化组件生命周期标志位
 initLifecycle(vm)
    // 初始化组件事件侦听
    initEvents(vm)
    // 初始化渲染方法
    initRender(vm)
    callHook(vm, 'beforeCreate')
    // 初始化依赖注入内容,在初始化data、props之前
    initInjections(vm) // resolve injections before data/props
    // 初始化props/data/method/watch/methods
    initState(vm)
    initProvide(vm) // resolve provide after data/props
    callHook(vm, 'created')

按顺序来讲的话就是

  1. 初始化生命周期标志位 作用:初始化生命周期的目的是建立出组件之间的关系,在实例对象上赋值$parent,$children等

  2. 初始化事件监听 作用:处理自定义事件,比如,父组件在子组件上自定义了监听事件,子组件会处理这个监听事件转换成this.$emit(...),this.$on(...),也就是说是子组件在监听。

  3. 初始化渲染方法 作用:待定....后续更新

  4. 生命周期钩子函数“beforeCreate”回调 作用:作为一个回调函数可供开发者使用

  5. 初始化injects在初始化数据之前 作用:对inject做初始化操作,简述大致流程。首先会获取inject的key转换成一个数组,然后遍历key的数组,在每一次的遍历中,都会根据组件之间的父子关系($parent,上文初始化生命周期标志位的时候,已经建立了这些连接)向上查找父组件的provide的值也就是。vm._provide的值,如果找到了,就会将值保存下来,如果没有找到,那么就会使用inject所设置的默认值。然后将数据设置为响应式的。

6.初始化数据state,包括data,methods,props,computed和watch 作用:将数据设置为响应式的(后续文章详细讲解)

  1. 初始化provide 作用:将组建上provide的值,设置到vm._provide上,目的是让inject向上查找可以找到

  2. 生命周期钩子函数“created” 作用:作为一个回调函数可供开发者使用,此时可以访问到数据。

第四步

if (vm.$options.el) {
      vm.$mount(vm.$options.el)
    }

最后一步就是判断options中是否有el,如果有el就会自动调用$mount函数挂载到el上,如果没有el,那么就需要手动调用$mount函数进行挂载。

注意

initinject先于initprovide

我们知道inject是向上寻找父组件的_provide属性的,但是inject的初始化操作确实在provide之前的,这可能造成一些疑问,实际上子组件是在父组件的$mount中进行渲染的,这个时候父组件的_provide早就准备好了,随时可以访问的。

initinject先于initSatte

实际上这样做的目的是让data中的数据可以访问可以依赖inject中获取的数据。

总结

1.在vue构造函数中调用_init函数

2.在_init函数中,首先会判断是不是根组件,如果不是根组件,就会将options扁平化,存在$options中,这样在查找的时候,会节省很多时间。如果是根组件,就会合并options,比如全局注册自定义指令,全局设置组件等。在合并options的时候会进行规范化的操作,然后是混入合并,返回新的options

3.initLifecycle进行生命周期标志位初始化,搞定组件之间的关系

4.initEvents处理自定义事件

5.initRender初始化渲染方法

6.生命周期"beforeCreate"函数回调

7.initInjections初始化inject

8.initState初始化数据,实现数据响应式

9.initProvide 初始化provide,供子组件的inject向上查找

10.生命周期"created"进行回调

11.判断options是否有el,然后决定是否自动调用$mount

这就是new Vue的初始化过程。