从源码上来看,Vue对象的每一个生命周期钩子前都干了什么?

1、从官方文档查看的Vue的生命周期钩子

上面的图片是从Vue的官网里面找到的,若是单从这张图去查看生命周期干了什么,咱们只能知道:vue

  • beforeMount前会对给出的模板进行编译(仅仅是在用Vue.js打包的时候会进行编译,在用脚手架的时候,在打包过程当中会把模板编译成为渲染函数,这样能够避免每次建立一个Vue对象的时候进行编译模板)
  • mounted后,Vue实例对象才是完整的对象,这个转改可以维持到beforeDestroy

可是咱们不知道的东西会有不少,我这里就举出几个问题:算法

  1. 父子节点的生命周期执行顺序是怎么样的?
  2. 每一个生命周期可以访问到的数据是否相同?
  3. 咱们知道修改了data后可以触发更新生命周期的钩子,那么究竟是怎样进行触发的?
  4. 咱们知道Vue的话是经过VNode来更新管理真实的DOM的,那么在上面的生命周期何时进行渲染的?

为何咱们要知道上面的状况?由于咱们可能会遇到一些问题,须要知道Vue里面的实现app

  • 通常状况下,咱们在组件初始化完毕的时候,也就是在mounted回调函数里面调用咱们初始化的执行代码(很大状况下是请求,也就是异步操做),可是若是如今有一个性能的需求,就是要求尽早地调用咱们初始化的代码,那么选择哪一个生命周期呢?这得根据每一个生命周期干的事情来决定
  • 对于一些全局同步的操做,好比解析路由来对应页面显示的内容等等这些操做,那么咱们是想要它尽早地完成,以便全局使用。

在这里的话,咱们能够对我提出的第一个问题进行解决。如今场景是这样的,咱们有一个父亲组件(app.vue),还有一个子组件(index.vue),父亲组件传给子组件一个abc属性(这个属性绑定父亲组件的abc数据),在建立页面后,设定定时器,1s后更新abc属性,两秒后将父组件进行销毁,那么会出现什么状况呢?dom

咱们打印出了以上的信息,能够进行回答上面提出的第一个问题:异步

  • 父组件老是先于子组件建立,可是慢于子组件挂载节点
  • 父组件老是在子组件进行更新完毕后再进行更新
  • 父组件销毁的时候会进行递归销毁子组件

以上三点都是很容易理解缘由的,这里就不进行解释了。咱们如今已经知道的父子组件的生命周期执行顺序了,接下来咱们看一下每一个生命周期钩子前干了什么?ide

2、源码上查看的生命周期钩子(钩子以前)

0.选项的合并

​ 经过Vue的官方文档,咱们有时候可以看到一个建立Vue实例的选项能够有不一样的写法,那么针对于不一样的写法,须要有一个工具来把这几个不一样的写法进行规范化,这就是选项的合并的所要进行处理的内容。函数

1.beforeCreate

在这个钩子函数前主要是对Vue实例进行属性的初始化工具

  • 生命周期状态、监听器的初始化,这些与业务逻辑无关
  • 处理父组件对本组件的自定义事件的监听,将其放到**_events**中
  • 对建立VNode函数进行绑定,在Vue实例对象中,是不会存在直接对DOM进行操做的,而是经过建立VNode,而后经过patch的方式对DOM节点进行操做,操做的工具方法什么的跟原生同样的,Vue工具类里面包含对这些方法的封装。
  • 对父组件传进来的属性(props)以及监听事件(v-on)进行深度观察

2.created

在这个钩子函数以前主要是对数据进行初始化而且进行监听操做:性能

  • Injectionprovide选项进行初始化
  • 对于用户定义的属性进行初始化,如下是按顺序来初始化,若是后面的属性名与前面相同的话,会报错:
    • props:传进来的属性是父组件过来的,这个属性不须要在子组件里面进行观察(由于父组件已经观察它了)
    • method:对设定的方法挂载到vm实例中,而且使用bind方法绑定函数上下文为vm对象,这也就是为何咱们使用箭头函数的方式的时候,也是可以经过this访问vm实例。
    • data:在选项的合并的时候将data处理成一个可执行的函数,这里的话,是将这个函数进行执行,获得组件的数据,而且将全部数据进行深度观测。
    • computed:建立一个lazy型的观察者,这个观察者是不会主动去触发的(若是计算属性是函数的话,每次须要拿到值的时候进行调用函数,若是是普通值的话,则会直接获取普通值)。这个计算属性是不能直接赋值操做的(getter方法被设置成一个空函数,因此怎么进行赋值也没有用),因此是个lazy型的观察者。computed的值只有在获取的时候才会执行而后拿到最新的值,因此它是一个懒求值。
    • watch:对传进来观察的对象的路径进行解析,拿到对应的属性。建立一个观察者,对这个属性进行观察,一旦属性发生变化的时候,会触发这个观察者,调用传进来的函数。

3.beforeMount

在挂载前的生命钩子前会进行元素绑定以及渲染函数的操做:this

  • vm.$el属性赋值
  • 渲染函数若是为空,进行建立空的方法

4.mounted

在本生命周期前,会进行渲染函数观察者的设定,当属性改变的时候,会直接反映到页面数据中。渲染函数观察者为何要在数据观察者设定后进行建立的话,这个等到后面的讲述Vue的观察者篇章的时候再说

这里要注意到一点,在建立观察者的时候,咱们能够经过传给观察者一个before方法,这样执行观察者的函数前调用before方法。渲染函数观察者会传进一个before方法,这个方法只有一句话,就是将本组件的声明周期改成beforeUpdate,也就是说在修改数据的时候,反映到页面以前,组件会先进入到beforeUpdate生命周期。

5.beforeUpdate

上面一点也说明了,在beforeUpdate前是用户的各项操做,只要修改了某个属性值,那么就会触发这个生命周期钩子。

6.updated

触发beforeUpdate后,这时候是在清空调用观察者队列(queue是存放如今时间全部须要进行回调的观察者的一个队列,固然渲染函数观察者也在里面)。整个队列执行完毕后会进行调用这个updated生命周期函数,在调用这个生命周期函数的时候,页面从新渲染已经结束了,因此在updated生命周期钩子函数前会作如下事情:

  • 打补丁(patch函数),这个函数首先是使用diff算法实现对dom操做的最小化,而后diff算法完成后,使用方法来针对于VNode修改dom节点内容。
  • 从新绑定修改后的dom节点,大家能够在绑定vm对象的dom节点的属性上看到**__vue__这个属性,这个就是指向绑定的vm**。

7.beforeDestroy

仅仅是判断是否正在被destroy,使用变量锁(单线程下变量便可做为锁,无需使用原子操做)防止重复操做。

8.destroyed

这里主要是对事件、观察者的卸载,在这个生命周期钩子函数前会执行如下事情:

  • 观察者的卸载(computedwatchrender)三个观察者进行卸载。
  • 对根数据的观察者的卸载。
  • VNode进行卸载,VNode的卸载会引起dom节点的删除。

9.destroyed以后

这里对于组件的产生的事件、监听的事情进行卸载,以及将对应的dom节点的**__vue__**属性设空,最后对父亲节点的解绑。

3、回答问题

  • 问题1在前面已经回答了,就不重复了

  • 问题2:每一个生命周期可以访问到的数据是否相同?

    回答是不,在beforeCreate根本就拿不到数据。

    可是在正常运行的声明周期函数中(beforeMountupdated),数据是一致的。这是在修改数据的时候,在一个事件循环中先观察者放到队列中,而后触发观察者回调队列的执行。不过在一个vm对象中,渲染函数观察者老是在队列最后执行的,并且执行beforeUpdate是在执行渲染函数观察者回调函数以前执行的。换句话说,触发beforeUpdate以前,$watch定义的方法已经执行完毕,该更新的数据已经更新了。

  • 咱们知道修改了data后可以触发更新生命周期的钩子,那么究竟是怎样进行触发的?

    在上面已经说明地很清楚了,若是有不懂的地方能够再去看一下整个流程。

  • 咱们知道Vue的话是经过VNode来更新管理真实的DOM的,那么在上面的生命周期何时进行渲染的?

    beforeUpdateupdated中间。

相关文章
相关标签/搜索