likes
comments
collection
share

从0到Vue3企业项目实战【05.vue生命周期】

作者站长头像
站长
· 阅读数 15

什么是生命周期?

引言

人有生老病死,生老病死就是人的生命周期,vue的生命周期也是如此,从创建到销毁的整个过程就是Vue的生命周期,我们要了解的是vue组件各生命周期会触发内置的钩子函数,我们可以在特定的钩子函数中执行特定的操作,如初始化数据等等

生命周期的分类

vue的生命周期分为四大阶段,8个方法

  • 阶段分为初始化,挂载,更新,销毁

  • 钩子函数分别为beforeCreate,created,beforeMount,mounted,beforeUpdate,updated,beforeDestroy,destroyed

两两对应与阶段

阶段方法方法
初始化beforeCreatecreated
挂载beforeMountmounted
更新beforeUpdateupdated
销毁beforeDestroydestroyed

带before即代表阶段前,如初始化前等等,其实我们只要记住四个就可以吧所有全部记住了

生命周期详解

这是来自于官网的生命周期图示,你不需要一开始就完全理解,当随着你学习的深入,这个图的参考价值会越来越高,当然现阶段还是介绍vue2的生命周期,其实领悟了vue2的生命周期vue3没有太大的改变

从0到Vue3企业项目实战【05.vue生命周期】

来我们一步步分析整个生命周期的过程

  1. newVue() -> vue实例化(组件其实也是一个vue实例)
  2. init Evemts & Lifecycle -> 初始化事件与生命周期函数
  3. beforeCreate -> 初始化前生命周期钩子函数被执行(此钩子函数响应时,data和method还未挂载在实例对象上,所有还不能使用)
  4. init injections&reactivity -> vue实例挂载data和methods等
  5. create -> 初始化生命周期钩子函数被执行,实例创建
  6. 接下来编译模板开始分析
  7. Has el options? 是否有el选项? -> 检查要挂载到哪里
  • 没有,调用$mount()方法
  • 有,继续检查template选项 即检查main.js中,是否有el选项 ,如果有就不需要$mount了两者效果相似,这一项确认了我们要觉得将vue编译好的模板挂载在id为app的组件上
new Vue({
  el: '#app', // 挂载在index.html的id叫app的标签上
  render: h => h(App),
})

new Vue({
  render: h => h(App),
}).$mount('#app')

  1. template选项检查(脚手架创建的vue项目基本上都是有的)
  • 有 -> 编译template返回render渲染函数
  • 无 -> 编译el选项对应标签作为tempate(要渲染的模板)
  1. beforeMount -> 虚拟Dom挂载成为真实Dom之前会执行的钩子函数
  2. Create vm$el and Replace "el" with it -> 吧虚拟Dom和渲染数据一并挂载到真实Dom上
  3. 真实Dom挂载完毕
  4. mounted -> 挂载生命周期钩子函数执行
  5. beforeUpdate -> 当data里的数据发生改变,更新Dom之前会执行的钩子函数
  6. Virtual Dom re-render and patch -> 虚拟Dom重新渲染.大补丁到真实得Dom
  7. updated -> 生命周期钩子函数被执行
  8. 每当有data中的数据发生改变,都会重复这个过程
  9. beforeDestroy -> 当$destroy()被调用 -> 组件Dom被移除(例如v-if)时调用的生命周期钩子函数
  10. Teardown watchers child components and event listeners -> 关闭数据监听器,子组件和事件侦听器
  11. destroyed -> 实例销毁最后执行的一个钩子函数

如何使用生命周期钩子函数?

准备工作

从0到Vue3企业项目实战【05.vue生命周期】 创建一个组件,我们通过该组件来逐步学习使用钩子函数,lifeCycle.vue,将该组件在App.vue中引入并使用 App.vue

<template>
  <div>
    <LifeCycle v-if="isShow"></LifeCycle>
    <button @click="isShow = !isShow">显隐组件</button>
  </div>
</template>

<script>
import LifeCycle from "./components/lifeCycle.vue";

export default {
  components: { LifeCycle },
  data () {
    return {
      isShow: true
    }
  }
}
</script>

<style>
</style>

lifeCycle.vue

<template>
  <div>
    <h1>look控制台的打印</h1>
    <p id="myp">{{msg}}</p>
    <ul id="myUl">
      <li v-for="(item,index) in arr" :key="index">{{item}}</li>
    </ul>
    <button @click="arr.push(666)">打上666</button>
  </div>
</template>

<script>
export default {

  data () {
    return {
      msg: "nice",
      arr: ['点赞', '转发', '三连'],
      timer: {}
    }
  },
  // 一 初始化
  // new Vue()以后 vue内部给实例对象添加一些属性与方法,data和methods初始化之前
  beforeCreate () {
    console.log("beforeCreate -- 执行", this.msg);
  },
  // 该生命周期触发代表着 实例已初始化完成,即一下内容可以在在created中使用, data.method.数据侦听,计算属性,方法,事件/侦听器的回调函数
  created () {
    console.log("created -- 执行", this.msg);
    this.timer = setInterval(() => {
      console.log('点赞收藏三连');
    }, 500)
  },
  // 二 挂载阶段
  // 真实Dom挂载之前
  beforeMount () {
    console.log("beforeMount -- 执行", document.getElementById("myp"));
    this.msg = '挂载阶段改了'
  },
  // 真实Dom挂载以后
  // 场景 : 需要拿到挂载后的真实Dom
  mounted () {
    console.log("mounted -- 执行", document.getElementById("myp"));
  },
  // 三 更新阶段
  // 当数据发生改变还未开始更新Dom时触发
  beforeUpdate () {
    console.log("beforeUpdate -- 执行", document.querySelectorAll("#myUl>li")[3]);
  },
  // 当数据发生改变并更新了Dom后触发
  updated () {
    console.log("updated -- 执行", document.querySelectorAll("#myUl>li")[3]);
  },
  // 四 销毁阶段
  //  组件Dom被移除之前
  //  移除全局事件,移除计时器等,防止内存泄露,eventBus移除事件 $off方法
  beforeDestroy () {
    console.log("beforeDestroy -- 执行");
    clearInterval(this.timer)
  },

  destroyed () {
    console.log("destroyed -- 执行");
  }
}
</script>

<style>
</style>

初始化阶段

  • beforeCreate触发时,仅创建了实例以及对生命周期函数与事件的初始化,此时是不能获取到data的值的,相同的计算属性,侦听器等也未初始化完成,该生命周期函数是都不能获取以及调用的
  • created组件已完成了初始化, 可以在该生命周期获取data的值,使用method,计算属性,侦听器等等,我们可以在此处进行一些简单的ajax请求对数据进行初始化等等,此时真实Dom还未进行挂载,所有有对Dom操作都不可进行
  beforeCreate () {
    console.log("beforeCreate -- 执行", this.msg);
  },
  created () {
    console.log("created -- 执行", this.msg);
    this.timer = setInterval(() => {
      console.log('点赞收藏三连');
    }, 500)
  },

我们在该生命周期获取了data中的数据我们可以通过控制台看到

从0到Vue3企业项目实战【05.vue生命周期】

挂载阶段

  • beforeMount是在真实Dom挂载前的一个生命周期钩子函数,此时还无法获取到Dom
  • mounted此时真实Dom已经挂载完成,可以在该生命周期中获取Dom,通常,我会更喜欢在该生命周期中执行数据初始化的ajax请求,第一呢created比起来mounted执行快不了几微秒,对性能的提升是微乎其微的,并且浏览器机制中渲染线程和js线程是互斥的,所以有可能ajax请求会打断渲染线程,所以请求放在mounted中更合适
  // 二 挂载阶段
  // 真实Dom挂载之前
  beforeMount () {
    console.log("beforeMount -- 执行", document.getElementById("myp"));
    this.msg = '挂载阶段改了'
  },
  // 真实Dom挂载以后
  // 场景 : 需要拿到挂载后的真实Dom
  mounted () {
    console.log("mounted -- 执行", document.getElementById("myp"));
  },

从0到Vue3企业项目实战【05.vue生命周期】 我们在beforeMount中尝试打印了一下Dom,然后返回的是null,由此可知,该生命周期确实为渲染真实Dom并且在该生命周期中修改了msg中的值,mounted中可以看到可以打印出来Dom了,并且msg的值也发生了修改

更新阶段

  • 组件Dom挂载后此时组件就会进入一个循环直到组件销毁,循环即每当data中的数据发生改变时,就会执行一次更新阶段的生命周期钩子函数
  • beforeUpdate当数据发生更新,还未更新Dom时触发
  • updated,当更新Dom后就会触发该生命周期钩子函数
  // 三 更新阶段
  // 当数据发生改变还未开始更新Dom时触发
  beforeUpdate () {
    console.log("beforeUpdate -- 执行", document.querySelectorAll("#myUl>li")[3]);
  },
  // 当数据发生改变并更新了Dom后触发
  updated () {
    console.log("updated -- 执行", document.querySelectorAll("#myUl>li")[3]);
  },

从0到Vue3企业项目实战【05.vue生命周期】

注意两者都是可以拿到最新的数据的,不同的是Dom是否更新而已

销毁阶段

  created () {
    console.log("created -- 执行", this.msg);
    this.timer = setInterval(() => {
      console.log('点赞收藏三连');
    }, 500)
  },
  
  beforeDestroy () {
    console.log("beforeDestroy -- 执行");
    clearInterval(this.timer)
  },

  destroyed () {
    console.log("destroyed -- 执行");
  }

我们在created中挂载了一个定时器,我们先看看假如不在beforeDestroy中清除定时器后会发生什么

从0到Vue3企业项目实战【05.vue生命周期】 可以看到组件销毁后我们设计的定时器依旧在不停的执行,这就是一个内存泄露,当我们在beforeDestroy中清除定时器后就解决了

从0到Vue3企业项目实战【05.vue生命周期】 当然,不管是beforeDestroy或者destroyed都可以做清除定时器的操作,两者区别也不是特别大,看业务场景选择使用吧

来看看我的其他章节吧,正在长更中