初探 Vue 生命周期和钩子函数

1,783 阅读3分钟

生命周期

生命周期函数就是 Vue 实例在某一个时间点会自动执行的函数。

简单来说就是好像把人的出生到死亡分成一个个阶段,你取名字肯定是在你出生阶段,而不是在成年阶段;你结婚肯定是在成年阶段,而不是在出生阶段;如果说你在出生阶段想去阶段,那肯定是不行的。 组件也是一样,在实例化的时特定阶段调用特定方法,调用的这个方法就是钩子函数。

钩子函数

钩子函数和回调函数有什么区别吗?

它们区别是:

js派函数监听事件 => 监听函数就是所谓的钩子函数 => 函数钩取事件:函数主动找事件 => 钩子函数

js预留函数给dom事件,dom事件调用js预留的函数 => 事件派发给函数:事件调用函数 => 回调函数

打个比方:

钩子函数:一个房间里的监控摄像头监控着每一个进入的人的面部特征,识别出了符合条件的人就触发警告(执行函数事件);

回调函数:可以看做是在一片地区埋了许许多多的地雷,一旦踩中了某个地雷(触发事件),地雷就会爆炸(执行函数事件)。

可以简单的理解为:

钩子函数是事件被动的监听,一旦条件触发就执行

回调函数是主动事件,执行函数体内容

生命周期探究

<template>
    <div>{{msg}}</div>
</template>

<script>
export default {
  name: 'HelloWorld',
  data () {
    return {
      msg: 'hello world',
      msg1: ''
    }
  },
  beforeCreate () {
    console.groupCollapsed('beforeCreate 创建前状态')
    console.log('%c%s', 'color:MediumVioletRed', 'el     : ' + this.$el)
    console.log(this.$el)
    console.log('%c%s', 'color:MediumVioletRed', 'data   : ' + this.$data)
    console.log(this.$data)
    console.log('%c%s', 'color:MediumVioletRed', 'msg: ' + this.msg)
    console.groupEnd()
  },
  created () {
    console.groupCollapsed('created 创建前状态')
    console.log('%c%s', 'color:MediumVioletRed', 'el     : ' + this.$el)
    console.log(this.$el)
    console.log('%c%s', 'color:MediumVioletRed', 'data   : ' + this.$data)
    console.log(this.$data.msg)
    console.log('%c%s', 'color:MediumVioletRed', 'msg: ' + this.msg)
    console.groupEnd()
  },
  beforeMount () {
    console.groupCollapsed('beforeMount 挂载前状态')
    console.log('%c%s', 'color:MediumVioletRed', 'el     : ' + this.$el)
    console.log(this.$el)
    console.log('%c%s', 'color:MediumVioletRed', 'data   : ' + this.$data)
    console.log(this.$data.msg)
    console.log('%c%s', 'color:MediumVioletRed', 'msg: ' + this.msg)
    console.groupEnd()
  },
  mounted () {
    console.groupCollapsed('mounted 挂载后状态')
    console.log('%c%s', 'color:MediumVioletRed', 'el     : ' + this.$el)
    console.log(this.$el)
    console.log('%c%s', 'color:MediumVioletRed', 'data   : ' + this.$data)
    console.log(this.$data.msg)
    console.log('%c%s', 'color:MediumVioletRed', 'msg: ' + this.msg)
    console.groupEnd()
    setTimeout(() => {
      this.$data.msg = '123'
    }, 5000)
  },
  activated () {
    console.groupCollapsed('activated 挂载后状态')
    console.log('%c%s', 'color:MediumVioletRed', 'el     : ' + this.$el)
    console.log(this.$el)
    console.log('%c%s', 'color:MediumVioletRed', 'data   : ' + this.$data)
    console.log(this.$data.msg)
    console.log('%c%s', 'color:MediumVioletRed', 'msg: ' + this.msg)
    console.groupEnd()
    setTimeout(() => {
      this.$data.msg = 'hello tiantian'
    }, 10000)
  },
  beforeUpdate () {
    console.groupCollapsed('beforeUpdate 更新前状态')
    console.log('%c%s', 'color:MediumVioletRed', 'el     : ' + this.$el)
    console.log(this.$el)
    console.log('%c%s', 'color:MediumVioletRed', 'data   : ' + this.$data)
    console.log(document.getElementById('app').innerHTML)
    console.log('%c%s', 'color:MediumVioletRed', 'msg: ' + this.msg)
    console.groupEnd()
  },
  updated () {
    console.groupCollapsed('updated 更新后状态')
    console.log('%c%s', 'color:MediumVioletRed', 'el     : ' + this.$el)
    console.log(this.$el)
    console.log('%c%s', 'color:MediumVioletRed', 'data   : ' + this.$data)
    console.log(document.getElementById('app').innerHTML)
    console.log('%c%s', 'color:MediumVioletRed', 'msg: ' + this.msg)
    console.groupEnd()
    setTimeout(() => {
      this.$destroy()
    }, 5000)
  },
  beforeDestroy () {
    console.groupCollapsed('beforeDestroy 实例销毁前状态')
    console.log('%c%s', 'color:MediumVioletRed', 'el     : ' + this.$el)
    console.log(this.$el)
    console.log('%c%s', 'color:MediumVioletRed', 'data   : ' + this.$data)
    console.log(document.getElementById('app').innerHTML)
    console.log('%c%s', 'color:MediumVioletRed', 'msg: ' + this.msg)
    console.groupEnd()
  },
  destroyed () {
    console.groupCollapsed('destroyed 实例销毁后状态')
    console.log('%c%s', 'color:MediumVioletRed', 'el     : ' + this.$el)
    console.log(this.$el)
    console.log('%c%s', 'color:MediumVioletRed', 'data   : ' + this.$data)
    console.log(document.getElementById('app').innerHTML)
    console.log('%c%s', 'color:MediumVioletRed', 'msg: ' + this.msg)
    console.groupEnd()
  }
}
</script>

beforeCreatecreated

beforeCreate:在实例初始化完成时,被执行

created:在初始化结束之后会再初始化一些外部注入和一些双向绑定相关的事情时,被执行

这两个钩子函数执行完之后,初始化基本完成了。

beforeCreate阶段,eldata都没有被挂载;而在created阶段,el还没被挂在,但data已经被挂载了,如下图所示:

这里el为啥没有被挂载呢?

看上图,在created执行完毕后,它会询问一个条件:你这个Vue实例里是否有el这个选项。

如果有就又会询问是否有template这个选项:

  • 如果没有template就会走右侧的分支,
    • 如果这个实例没有template,就会将el这个根节点当做模版,来进行渲染
  • 如果有template就会走左侧的分支
    • template作为模版去渲染

beforeMountmounted

beforeMount:执行时,页面还没有被渲染 mounted:执行时,页面已经被渲染了

从图中也可以看出,在beforeMount执行时,el还没有被挂在;当mounted执行时,el被挂载到页面了。

beforeUpdateupdated

beforeUpdate:数据被改变,还没渲染之前会被执行

updated:数据被改变,渲染完成后会被执行

这张图中有个奇怪的现象,为什么在beforeUpdateupdated两个钩子函数中,elmsg都是一样呢?beforeUpdate执行是不应该是老数据嘛,怎么这里也是最新的数据了?

因为这里的el是虚拟dom,不是真实的dom,和data都是对象,在加上console.log这里是个异步操作,当你点开console.log时,其实代码早就跑完了,数据已经是最新的了,所以就会看到在这两个函数中输出结果是一样的了。

可以用document.getElementById('app').innerHTML获取真实的Dom结构,这时我们就可以看到这两处不一样的地方了。

beforeDestroydestroyed

调用vm.$destroy()方法可对实例销毁

beforeDestroy:实例被销毁前被执行

destroyed:实例被销毁后被执行

activateddeactivated

使用keep-alive标签后,会有两个生命周期函数分别是:activateddeactivated

activated:页面展示的时候被执行

deactivated:页面被隐藏或者页面即将被替换成新的页面时被执行

总结

created:挂载之前需要做的一些事情可以在放在这里面,比如页面加载时loading动画

mounted:向后端发请求,可以放在这个函数中。

这两个钩子函数使用时机重叠部分很多,反正是怎么方便怎么来就是了。

参考资源

  1. Vue2.0 探索之路——生命周期和钩子函数的一些理解
  2. vue2 为什么beforeUpdate时的el 和data与updated时的一样
  3. JavaScript:钩子函数与回调函数的区别