Vue实例生命周期

129 阅读4分钟

1. 生命周期图示

  1. 先展示一波vue实例生命周期图示

生命周期.png 2. vue实例从创建到销毁可能会经历的生命周期钩子函数有:

  • beforeCreate
  • created
  • beforeMount
  • mounted
  • beforeUpdate
  • updated
  • beforeDestroy
  • destroyed
  • activated
  • deactivated
  • errorCaptured

2. 生命周期钩子函数具体介绍与功能实现

2.0 生命周期钩子函数总结

1.PNG

2.1 操作数据的钩子函数
  • 操作数据的钩子函数包括beforeCreate、created;
  1. beforeCreate
  • 定义:在实例初始化之后,进行数据侦听和事件/侦听器的配置之前同步调用;
  • 调用beforeCreate时,数据的值还是undefined,并无法进行操作数据的动作;
  1. created
  • 定义:数据侦听、计算属性、方法、事件/侦听器的回调函数已经配置完毕;
  • 调用created时,数据已经被赋值,此时可以进行操作数据的动作;
  • 但此时挂载阶段还没有开始;
  1. 代码展示
  • 创建一个LifeCycle组件,用来展示实例生命周期函数效果;
<template>
    <div class="box">
        <div>生命周期{{ text }}</div>
    </div>
</template>
<script>
export default {
    name:'MyLifeCycle',
    data() {
        return {
            text: "hello"
        }
    },
    // 操作数据
    beforeCreate() {
        console.log(this.text);// undefined 无法操作数据
        console.log('【初始必备】1:beforeCreate');
    },
    created() {
        console.log(this.text); // 可以操作数据
        console.log('【初始必备】2:created');
    }
}
</script>

此时控制台打印出的结果是:

image.png

2.2 操作DOM的钩子函数
  • 操作数据的钩子函数包括beforeMount、mounted
  1. beforeMount
  • 定义:在挂载开始之前被调用,此时相关的render函数首次被调用;
  • 此时的this.$el的值是undefined,还无法进行操作DOM的动作;
  1. mounted
  • 定义:实例被挂载后调用,这时 el 被新创建的 vm.$el 替换了;
  • 此时,this.$el是可以获取到DOM元素的;
  • 注意:此时,并不能保证所有的子组件也被挂载完毕,如果希望整个视图都被渲染完毕后,在进行一些操作,可以在mounted函数中使用$nextTick;
  1. 代码展示

// 操作DOM
beforeMount() {
  console.log(this.$el); // undefined不适合操作DOM
  console.log('【初始必备】3:beforeMount');
},
mounted() {
  console.log(this.$el); // 获取dom元素
  // 改变按钮的颜色
  // 在数据变更以后,渲染之后的下一次行为中执行
  this.$nextTick(()=>{
      this.$refs.btn.style.color = 'red';// 此时元素没有存在
  });
  console.log('【初始必备】4:mounted');
}

此时控制台打印出的结果是:

image.png

2.3 更新时调用的钩子函数
  • 更新时调用的钩子函数有beforeUpdate、updated;
  1. beforeUpdate
  • 定义:在数据发生改变后,DOM 被更新之前被调用;
  • 此时this.$el指向更新前的DOM,可以在这里进行一些DOM元素修改前要进行的操作;
  1. updated
  • 定义:在数据更改导致的虚拟 DOM 重新渲染和更新完毕之后被调用;
  • 此时,已经可以拿到更新完成的DOM元素;
  • 注意:在此期间应该避免进行更改状态(数据)的操作;
  • 如需进行更改数据操作,应该选择computed或者watch属性来实现;
  • 同样的,此时并不能保证所有子组件都被挂载完毕了,如果想在所有元素挂载完成后进行一些操作,可以在此钩子函数中使用$nextTick;
  1. 代码展示
  • 增加空值text值修改的按钮组件,方便测试
<template>
    <div class="box">
        <div>生命周期{{ text }}</div>
        <button @click="text = 'Hello Green'">修改text</button>
    </div>
</template>

// 更新时
beforeUpdate() {
  console.log(this.$el); // 更新前的DOM
  console.log('【发生修改】5:beforeUpdate');
},
updated() {
  console.log(this.$el); // 更新后的DOM
  console.log('【发生修改】6:updated');
}

此时控制台打印出的结果是: image.png

2.4 组件销毁时调用的钩子函数
  • 组件销毁时时调用的钩子函数有beforeDestroy、destroyed;
  1. beforeDestroy
  • 实例销毁之前调用,此时实例还是完全可用的;
  • 注意:该钩子在服务器端渲染期间不被调用;
  • 可以在此阶段手动进行DOM/定时器销毁事件,或者手动清除一些大数据,保证及时清除内存空间;
  1. destroyed
  • 实例销毁后调用,此时所有实例包括子实例也都被销毁,vue指令都将失效;
  • 一般没有必须在此阶段进行的操作;
  1. 代码展示
beforeDestroy() {
  // DOM事件/定时器
  // clearInterval(this.timer);
  // this.bigData.length = 0; // 做了一大数据处理的注释
  console.log('【销毁必备】7:beforeDestroy');
},
destroyed() {
  console.log('【销毁必备】8:destroyed');
}
2.5 被keep-alive缓存组件的激活与失活钩子函数
  1. keep-alive
  • 定义:是一种vue内置组件,作用就是缓存需要频繁变换状态的组件状态;
  • 在keep-alive内包裹的组件,可以触发activated和deactivated 2个生命周期钩子函数;
  • 作用在于避免频繁的创建与销毁组件, 缓存组件数据;
  • 一些属性:
  • 排除掉哪些组件 :exclude=“[组件名HelloWorld]” ,components中决定;
  • 包含哪些组件: :include=“[组件名HelloWorld]” ,components中决定;
  • max值为数字,表示最多缓存多少个组件, 是一种临界值 :max=“9”表示最多缓存9个组件,该属性主要为了避免缓存组件较多占用较大内存,造成卡顿;
  1. activated
  • 定义:被 keep-alive 缓存的组件激活时调用;
  • 相当于created的替换,被缓存的组件不用通过created函数创建,而是通过 activated钩子函数进行状态激活;
  1. deactivated
  • 定义:被 keep-alive 缓存的组件失活时调用;
  • 相当于deactivated的替换,隐藏被缓存的组件时,不用通过beforeDestroy函数销毁组件,而是通过deactivated钩子函数将组件状态设置为失活;
  1. 代码展示
  • 在父组件中调用LifeCycle组件时,用keep-alive组件包裹;
  • 定义一个button组件控制组件状态;
<keep-alive : include = "cached" exclude = "ACompoment" : max = "99">
  <LifeCycle v-if="isExist" v-model="isExist" @close="doClose" />
</keep-alive>
<button @click="open">切换生命周期组件</button>
  • 在父组件的components中声明引入的子组件,在cached数组中添加需要缓存状态的组件;
export default {
  components: { LifeCycle },
  data() {
    return {
      cached: [
        'MyLifeCycle'
      ],
      isExist: true
    }
  },
}
  • 在父组件定义实现组件控制和组件状态转换的方法;
methods: {
  open() {
    this.isExist = !this.isExist;
    if (!this.cached.includes('MyLifeCycle'))
      this.cached.push('MyLifeCycle')
  },
  doClose(url) {
    this.cached.splice(this.cached.indexOf(url), 1);
  }
}
  • LifeCycle组件中调用activated和deactivated钩子函数;
<template>
    <div class="box">
        <span style="position:absolute;right:6px;top:4px;cursor:pointer;" @click="close">X</span>
    </div>
</template>
<script>
export default {
    name:'MyLifeCycle',
    methods:{
        close(){
            this.$emit('input',false);
            this.$emit('close','MyLifeCycle');
        },
    activated() { // created的替换
        console.log('【特殊场景】9:activated');
    },
    deactivated() { // beforeDestroy的替换
        console.log('【特殊场景】10:deactivated');
    }
}
</script>
  • 界面效果: image.png
  • 点击切换生命周期按钮时,控制台打印如下: image.png
  • 点击右上角X,先取消组件缓存,此时再点击切换生命周期按钮时控制台打印: image.png
2.6 errorCaptured(异常捕获钩子函数)
  • 定义:捕获一个来自后代组件的错误时被调用,放到父组件, 就能捕获子组件, 非 异步的异常;
  • 此函数可以返回 false 以阻止该错误继续向上传播;
  • 一般在根组件return false,做日志的输出;
  • 用来做错误日志收集系统,发ajax请求: 账号, 时间, 错误, 前后访问的页面行为;
  1. 代码展示
errorCaptured(e) {
  console.log('【异常捕获】11:errorCaptured', e);
  return false; // 再向上传播 就到了浏览器
}