Vue 生命周期与钩子函数

4,634 阅读4分钟

文章已同步至【个人博客】,欢迎访问😃
文章地址:blog.fanjunyang.zone/archives/vu…

如果想要了解对于实现页面逻辑交互等效果,我们必须知道 Vue 的生命周期,知道我们写的东西应该挂载到哪里?而且说到生命周期,必定少不了钩子函数。。

Vue官方给出的api讲解:

所有的生命周期钩子自动绑定this上下文到实例中,因此你可以访问数据,对属性和方法进行运算。这意味着你不能使用箭头函数来定义一个生命周期方法(例如created: () => this.fetchTodos())。这是因为箭头函数绑定了父上下文,因此this与你期待的 Vue 实例不同,this.fetchTodos的行为未定义。

好像听不太懂?看下面

生命周期

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

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

钩子函数

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

区别:

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

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

可以简单的理解为:

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

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

敲黑板

重点图来了,不说废话,附上:

是不是有那么一丁点了解了

上猛料:

Vue中有8中生命周期函数:

就这八种

不多说了,来一组测试代码

注:此处省略部分代码

<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:在初始化结束之后会再初始化一些外部注入和一些双向绑定相关的事情时,被执行。
当前组件已构造完成,已经完成了数据劫持,把方法和计算属性都已经挂载到了当前的组件上,但是并不能获取到真实的DOM,所以不能操作DOM,通常会完成Ajax请求

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

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

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

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

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

  • 如果没有 template 就会走右侧的分支,

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

    • 把 template 作为模版去渲染

beforeMountmounted

beforeMount:执行时,页面还没有被渲染。

mounted:执行时,页面已经被渲染了。
真实DOM已完成,挂载到了页面上,也可以发起Ajax请求

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

beforeUpdateupdated

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

updated:数据被改变,渲染完成后会被执行。
不能修改data中的数据,可能会造成死循环

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

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

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

beforeDestroydestroyed

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

beforeDestroy:实例被销毁前被执行。

destroyed:实例被销毁后被执行。

activateddeactivated

对了,也有人说这两个也是生命周期函数,那就来看看

使用keep-alive标签后,会有两个生命周期函数分别是:activated、deactivated
具体keep-alive的使用方法,请参考我的文章《Vue中keepalive的深入理解和使用》

activated:页面渲染的时候被执行。

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

当引入keep-alive的时候,页面第一次进入,钩子的触发顺序 created-> mounted-> activated,退出时触发deactivated。当再次进入(前进或者后退)时,只触发activated。

事件挂载的方法等,只执行一次的放在 mounted 中;组件每次进去都执行的方法放在 activated 中, activated 中的方法不管是否需要缓存都会执行。


简单说一下:

  • beforecreate : 可以在这加个loading事件,在加载实例时触发
  • created : 初始化完成时的事件写在这里,如在这结束loading事件,异步请求也适宜在这里调用
  • mounted : 挂载元素,获取到DOM节点
  • updated : 如果对数据统一处理,在这里写上相应函数
  • beforeDestroy : 可以做一个确认停止事件的确认框
  • nextTick : 更新数据后立即操作dom

^_<