一、生命周期的概念

生命周期是指从开始创建、初始化数据、编译模版、挂载 Dom -> 渲染、更新 -> 渲染、卸载等一系列过程,我们称这是 Vue 的生命周期,它主要强调一个时间段。用一句话来概括就是:Vue实例的生命周期: 从创建到销毁的整个过程

二、钩子函数

Vue框架内置函数,随着组件的生命周期阶段,自动执行

作用:特定的时间点执行特定的操作

三、组件的生命周期

1 生命周期的阶段划分

image.png

  • (1)创建阶段:beforeCreate、created、beforeMount、mounted ​* (2)运行阶段:beforeUpdate、updatevef​
  • (3)销毁阶段:beforeDestroy、destroyed

3、生命周期函数

由Vue提供的内置函数,伴随组件的生命周期按次序自动运行——钩子函数。

4、钩子函数:

vue的内置函数,当Vue组件运行到某个阶段时这些函数会自动运行,不需要用户显式的调用

  • (1)beforeCreate:在组件创建之前运行,此时Vue实例的el、data、data中的变量均为undefined
  • (2)created:表示组件已经创建完成,data、props已经初始化了,el还是undefined(组件还没有挂载到DOM上)
  • (3)beforeMount:el被绑定(和DOM绑定),但未挂载
  • (4)mounted:组件挂载之后,el绑定、组件挂载
  • (5)beforeUpdate:当组件的内容被改变、隐藏的组件被显示、显示的组件被隐藏等触发
  • (6)update:由于数据更改导致的虚拟 DOM 重新渲染和打补丁,在这之后会调用该钩子
  • (7)beforeDestroy:在组件销毁之前。组件还是正常使用
  • (8)destroyed:组件销毁之后

5、keep-alive组件

是vue的内置组件,用来保持vue组件运行的某种状态,避免被重新渲染 强调:组件(页面的组成部件)

  • 第一步:创建组件(定义组件),组件文件的扩展名(后缀)可以是.vue,也可以是.js
  • 第二步:在使用组件的位置导入,注册组件
  • 第三步:使用组件:像html标签一样使用

6、生命周期图示

image.png

7、生命周期代码演示

1.初始化阶段(Creation)

  • 1.beforeCreate:这是生命周期所执行的第一个钩子函数,执行于组件实例被创建之初,data 和 methods 中的数据还没有初始化。
  • 2.created:在实例创建完成后被调用,此阶段完成了数据观测 (data observer)、属性和方法的运算,以及 watch/event 事件的设置。但是此时还没有挂载到 DOM 上。
  • 3.代码和效果如下:
<template>
  <div>
    <div id="test">{{name}}</div>
  </div>
</template>
 
<script>
  export default {
    data() {
      return {
        name: '暴怒的代码'
      }
    },
    beforeCreate() {
      console.log('------------------初始化阶段------------------')
      //这里data 和 methods 中的数据还没有初始化,所以输出的应该是undefined
      console.log(`这是beforeCreate的执行:${this.name}`)
    },
    created() {
      //这里数据已经初始化了,可以输出data中的内容
      console.log(`这是Create的执行:${this.name}`)
      //因为在created()中还没有渲染到DOM页面,所以在渲染前修改,选的数据是下面的内容
      this.name = '在渲染到DOM之前就修改内容'
    }
  }
</script>

image.png

2.挂载阶段(Mounting)

  • 1.beforeMount:模板渲染,相关的 render 函数首次被调用,模板已经在内存中编译好了,但是尚未挂载到页面中去。
  • 2.mounted:在实例被挂载到 DOM 后调用,真实DOM生成,此阶段完成了模板编译并且将实例挂载到 DOM 上。
  • 3.代码和效果如下:
<template>
  <div>
    <div id="test">{{name}}</div>
  </div>
</template>
 
<script>
  export default {
    data() {
      return {
        name: '暴怒的代码'
      }
    },
    beforeCreate() {
      console.log('------------------初始化阶段------------------')
      //这里data 和 methods 中的数据还没有初始化,所以输出的应该是undefined
      console.log(`这是beforeCreate的执行:${this.name}`)
    },
    created() {
      //这里数据已经初始化了,可以输出data中的内容
      console.log(`这是Create的执行:${this.name}`)
      //因为在created()中还没有渲染到DOM页面,所以在渲染前修改,选的数据是下面的内容
      this.name = '在渲染到DOM之前就修改内容'
    },
    beforeMount() {
      console.log('------------------挂载阶段------------------')
      //开始编译数据,但是当前数据只在内存中渲染,并没有真实的渲染到html页面上
      console.log(`这是beforeMount的执行:${document.getElementById('test').innerText}`)
    },
    mounted() {
      // 获取到数据,内存中的数据真实的挂载到页面中,Vue实例创建完成
      console.log(`这是mounted的执行:${document.getElementById('test').innerText}`)
    }
  }
</script>

image.png

注意:mounted 不会保证所有的子组件也都被挂载完成。如果你希望等到整个视图都渲染完毕再执行某些操作,可以在 mounted 内部使用 vm.$nextTick

3.更新阶段(Updating)

  • 1.beforeUpdate:在数据更新之前被调用,发生在虚拟 DOM 重新渲染和打补丁之前,此阶段页面中渲染的数据还是旧的,但data里的数据是最新的,页面尚未和最新数据保持同步。
  • 2.updated:在数据更新完成后被调用,实例的 DOM 已经更新。
  • 3.代码和效果如下:
<template>
  <div>
    <div id="test">{{name}}</div>
    <el-button @click="name='点击更新的内容'">更新内容</el-button>
  </div>
</template>
 
<script>
  export default {
    data() {
      return {
        name: '暴怒的代码'
      }
    },
    beforeCreate() {
      console.log('------------------初始化阶段------------------')
      //这里data 和 methods 中的数据还没有初始化,所以输出的应该是undefined
      console.log(`这是beforeCreate的执行:${this.name}`)
    },
    created() {
      //这里数据已经初始化了,可以输出data中的内容
      console.log(`这是Create的执行:${this.name}`)
      //因为在created()中还没有渲染到DOM页面,所以在渲染前修改,选的数据是下面的内容
      this.name = '在渲染到DOM之前就修改内容'
    },
    beforeMount() {
      console.log('------------------挂载阶段------------------')
      //开始编译数据,但是当前数据只在内存中渲染,并没有真实的渲染到html页面上
      console.log(`这是beforeMount的执行:${document.getElementById('test').innerText}`)
    },
    mounted() {
      //获取到数据,内存中的数据真实的挂载到页面中,Vue实例创建完成
      console.log(`这是mounted的执行:${document.getElementById('test').innerText}`)
    },
    beforeUpdate() {
      console.log('------------------更新阶段------------------')
      //页面上的数据还是以前的,内存中的数据是最新的
      console.log(`这是beforeUpdate的执行,页面数据:${document.getElementById('test').innerText}`)
      console.log(`这是beforeUpdate的执行,data数据:${this.name}`)
    },
    updated() {
      //页面上的数据和内存中的数据都是最新的
      console.log(`这是updated的执行,页面数据:${document.getElementById('test').innerText}`)
      console.log(`这是updated的执行,data数据:${this.name}`)
    }
  }
</script>

image.png

注意:updated 不会保证所有的子组件也都被重新渲染完毕。如果你希望等到整个视图都渲染完毕,可以在 updated 里使用 vm.$nextTick

注意:不要在updated中修改data数据,很容易造成死循环。

4.销毁阶段(Destruction)

  • 1.beforeDestroy:在实例销毁之前调用,此时实例仍然完全可用,还没有真正的执行销毁,vue从运行阶段进入到销毁阶段。
  • 2.destroyed:在实例销毁后调用,此阶段完成了实例的事件监听器和子组件的销毁,vue上所有的实例都不可以用了。
  • 3.代码和效果如下:
<template>
  <div>
    <div id="test">{{name}}</div>
    <el-button @click="name='点击更新的内容'">更新内容</el-button>
  </div>
</template>
 
<script>
  export default {
    data() {
      return {
        name: '暴怒的代码'
      }
    },
    render(h) {
      return h('div', [
        h('h1', this.name),
        h('button', {
          on: {
            click: () => {
              alert('点击了我')
            }
          }
        }, 'Click me')
      ]);
    },
    beforeCreate() {
      console.log('------------------初始化阶段------------------')
      //这里data 和 methods 中的数据还没有初始化,所以输出的应该是undefined
      console.log(`这是beforeCreate的执行:${this.name}`)
    },
    created() {
      //这里数据已经初始化了,可以输出data中的内容
      console.log(`这是Create的执行:${this.name}`)
      //因为在created()中还没有渲染到DOM页面,所以在渲染前修改,选的数据是下面的内容
      this.name = '在渲染到DOM之前就修改内容'
    },
    beforeMount() {
      console.log('------------------挂载阶段------------------')
      //开始编译数据,但是当前数据只在内存中渲染,并没有真实的渲染到html页面上
      console.log(`这是beforeMount的执行:${document.getElementById('test').innerText}`)
    },
    mounted() {
      //获取到数据,内存中的数据真实的挂载到页面中,Vue实例创建完成
      console.log(`这是mounted的执行:${document.getElementById('test').innerText}`)
    },
    beforeUpdate() {
      console.log('------------------更新阶段------------------')
      //页面上的数据还是以前的,内存中的数据是最新的
      console.log(`这是beforeUpdate的执行,页面数据:${document.getElementById('test').innerText}`)
      console.log(`这是beforeUpdate的执行,data数据:${this.name}`)
    },
    updated() {
      //页面上的数据和内存中的数据都是最新的
      console.log(`这是updated的执行,页面数据:${document.getElementById('test').innerText}`)
      console.log(`这是updated的执行,data数据:${this.name}`)
    },
    beforeDestroy() {
      console.log('------------------销毁阶段------------------')
      // 仍可以使用data与method方法
      console.log(`这是beforeDestroy的执行:${this.name}`)
    },
    destroyed() {
      // 已经销毁data与method方法
      console.log(`这是destroyed的执行:${this.name}`)
    },
  }
</script>

5.两个特殊的生命周期钩子函数

vue2中有两个特殊的生命周期钩子函数, 被 keep-alive 缓存的组件 激活时调用。

  • activated: keep-alive 组件专属,组件激活时调用该函数。
  • deactivated:keep-alive 组件专属,组件被销毁时调用该函数。

6.errorCaptured

在捕获一个来自后代组件的错误时被调用。此钩子会收到三个参数:错误对象、发生错误的组件实例以及一个包含错误来源信息的字符串。此钩子可以返回 false 以阻止该错误继续向上传播。

总结

生命周期是vue2中一个很重要的知识点,同时也是面试中容易问到的高频面试点。熟练掌握了vue2的生命周期,可以让我们在项目开发过程中少走很多弯路,减少很多莫名其妙的bug。

参考: https://blog.csdn.net/Gik99/article/details/129335586

https://blog.csdn.net/c18559787293/article/details/132496580