Vue的生命周期是不仅是面试必考点,理解它更能为我们在Vue项目开发带来促进作用。

什么是Vue的生命周期?

每个 Vue 实例在被创建时都要经过一系列的初始化过程——例如,需要设置数据监听、编译模板、将实例挂载到 DOM 并在数据变化时更新 DOM 等。同时在这个过程中也会运行一些叫做生命周期钩子的函数,这给了用户在不同阶段添加自己的代码的机会。简单理解就是Vue实例从创建到销毁的这么一个过程。如图(官网)所示:

Vue生命周期函数(钩子)执行顺序

从上图可知主要为八大生命周期钩子:beforeCreate、created、beforeMount、mounted、beforeUpdate、updated、beforeDestroy、destroyed。执行顺序为上图所示顺序。

验证生命周期钩子执行顺序:

new Vue({
  el: '#app',
  router,
  store,
  data: {
    greet: 'hello world'
  },
  template: '<div>我是模板内的{{greet}}</div>',
  beforeCreate() {
    console.log('------beforeCreate------')
  },
  created () {
    console.log('------created------')
  },
  beforeMount () {
    console.log('------beforeMount------')
  },
  mounted () {
    console.log('------mounted------')
  },
  beforeUpdate () {
    console.log('------beforeUpdate------')
  },
  updated() {
    console.log('------updated------')
  },
  beforeDestroy() {
    console.log('------beforeDestroy------')
  },
  destroyed() {
    console.log('------destroyed------')
  }
})

打印结果如下:

我们发现只打印出了四个钩子中的信息,即首次创建实例过程中,只执行四个生命周期钩子,beforeCreate、created、beforeMount、mounted,执行顺序与生命周期图一致,beforeUpdate与updated是在数据更新时候执行,而beforeDestroy与destroyed是在实例销毁的时候执行。在每个生命周期钩子执行期间或者之后都会进行一些相关的操作,以下分析这期间都做了什么工作。

生命周期函数(钩子)详解

结合生命周期图,进一步分析。

new Vue()

 

创建一个vue实例,然后init初始化event 和 lifecycle,期间分别调用了3个初始化函数(initLifecycle(), initEvents(), initRender()),初始化了生命周期,事件以及定义createElement函数。在初始化生命周期时,定义了一些属性,比如生命周期状态_isMounted ,_isDestroyed ,_isBeingDestroyed,表示keep-alive中组件状态的_inactive。初始化event,定义了$once、$off、$emit、$on几个函数。createElement函数是在初始化render时定义的(调用了initRender函数)。

beforeCreate

beforeCreate执行结束后,数据初始化,定义data数据、方法及事件,并且通过Object.defineProperty()来实现数据劫持以及给组件实例配置watcher观察者实例(发布-订阅者模式),即实现数据双向绑定,后续当数据发生变化时,感知数据变化并完成页面渲染。

示例:

beforeCreate() {
  console.log('------beforeCreate------')
  console.log(this.greet)
  console.log(this.$el)
}

  

created

该钩子中我们可以拿到data下的数据以及methods下的方法了。在这个钩子中,我们可以开始调用方法进行数据请求了。

created执行结束后,判断当前是否有el参数,如果有,继续判断是否有template参数。如果没有el,那么我们会等待调用$mount(el)方法(即手动挂载)。
确保有了el后,往下判断当有template参数时,将template模板转换成render函数(在此之前判断当前是否有render函数,如果有,则会直接去渲染当前的render函数,如果没有则查找是否有template模板),如果没有template,将直接获取到的el(也就是我们常见的#app)编译成template,然后在将这个template转换成render函数。

示例:

created () {
  console.log('------created------')
  console.log(this.greet)
  console.log(this.$el)
}

  

beforeMount

在creted到beforeMount之间,最主要的工作就是将模板或者el转换为render函数。这里需要注意,不管是用el,还是用template,或者是.vue文件(如果是.vue文件,会先编译成template),最终都会被转换为render函数。

beforeMount执行后,开始渲染render函数了,此时先产生一个虚拟dom(用于后续数据发生变化时,新老虚拟dom对比diff算法),进行保存,然后再开始将render渲染成为真实的dom,替换掉原来的vm.$el,再将替换后的$el append到我们的页面内。基本流程走完。

示例:

beforeMount () {
  console.log('------beforeMount------')
  console.log(this.greet)
  console.log(this.$el)
}

此时$el为“#app”中对应的元素:

 

之后向下检查到template,用其将#app替换,转换成render函数,渲染成真实dom后,替换了原来的$el。往下观察mounted打印信息。

mounted

执行mounted,将标识生命周期的一个属性_isMounted 置为true。在mounted钩子内,我们可以操作dom,因为这个时候dom已经渲染完成了。

示例:

mounted () {
  console.log('------mounted------')
  console.log(this.greet)
  console.log(this.$el)
}

上面说到检查到template,转换成render函数,渲染成真实dom后,替换了原来的$el(#app)。

beforeUpdate、updated

当数据变化时,触发beforeUpdate,将我们变化后的数据渲染到页面上了(此时判断当前的_isMounted是不是为ture且_isDestroyed是不是为false,保证dom已经被挂载的情况,且当前组件并未被销毁,才会执行update)
beforeUpdate执行结束之后,重新生成一个新的虚拟dom(Vnode),用它和原来的Vnode做比较(diff算法),更新render函数中的数据,之后将render函数渲染成真实dom,完成页面更新。
继续执行updated,此时updated里面可以获取到更新之后的dom且可以对其进行操作。

示例:

添加一个改变数据的方法

 

未点击按钮

更新钩子函数打印信息

beforeUpdate () {
  console.log('------beforeUpdate------')
  console.log(this.greet)
  console.log(this.$el)
},
updated() {
  console.log('------updated------')
  console.log(this.greet)
  console.log(this.$el)
}

点击按钮后

 

beforeDestroy、destroyed

beforeDestroy钩子内仍可以该实例仍可用,之后会执行一系列的销毁动作,解除各种数据引用,移除事件监听,删除组件_watcher,删除子实例等。同时将实例属性_isDestroyed置为true。销毁完成后,执行destroyed。生命周期阐述完毕。

tips:实例挂载方式

new Vue({
  el: '#app',
  router,
  store,
  render: h => h(App)
})
new Vue({
  router,
  store,
  render: h => h(App)
}).$mount('#app')     //不存在el,手动调用$mount进行挂载

以上生命周期为Vue2.x版本,Vue3.x版本生命周期有所改变,在此不展开,有兴趣的可以自行了解,附上两个版本生命周期对比图:

Logo

前往低代码交流专区

更多推荐