Vue3.0组件的生命周期:VOA模式与VCA模式

简介

每个Vue 组件实例在创建时都需要经历一系列的初始化步骤,比如设置好数据侦听,编译模板,挂载实例到DOM,以及在数据改变时更新 DOM。在此过程中,它也会运行被称为生命周期钩子的函数,让开发者有机会在特定阶段运行自己的代码。

VCA模式下的生命周期:Options API

<template>
  <div id="myid">

  </div>
</template>
<script>
import AChild from "./components/AChild.vue" //导入AChild组件模板
export default {
  components: {
    AChild
  },
  data() {
    return {
      name: "lily"
    }
  },
  beforeCreate() {
    //这个钩子函数会在实例初始化完成props 解析之后,data() 和 computed 等选项处理之前立即调用。
    //beforeCreate()创建前,视图view,data模型都未被创建

    //console.log(this.name)  //此时无法访问data中的数据
    //this.myClick();         //此时无法访问methods中的方法


    //用途:
    //1、可以做页面拦截。当进一个路由的时候我们可以判断是否有权限进去,是否安全进去,携带参数是否完整,参数是否安全。使用这个钩子好函数的时候就避免了让页面去判断,省掉了创建一个组建Vue实例。
    //2、可以做自定义重定向。当路由还没有进去时我们判断是否正确进去,若不正确进去可以重定向到指定的页面。
    //3、依赖注入:在beforeCreate钩子函数中,你可以访问到组件的依赖注入,例如通过this.$options.inject 来获取注入的属性或方法。这可以用于实现组件之间的通信或共享数据。

  },
  created() {
    //当这个钩子被调用时,以下内容已经设置完成:响应式数据、计算属性、方法和侦听器。然而,此时挂载阶段还未开始,因此不能在这里操作Dom模板:document.getElementById("myid")
    //beforeCreate()创建前,视图view,data模型都未被创建

    console.log(this.name) //此时已经可以访问data中的数据了
    this.myClick();        //此时已经可以访问methods中的方法了

    //一般用在对data中的数据做初始化处理:比如发起ajax请求数据赋值给data中的变量
  },
  beforeMount() {
    //组件挂载到节点之前执行的函数,此时无法用document.getElementById,因为此时组件还未挂载到节点
    //注释:假如一个页面相当于一棵树,组件就是它的枝叶,当前的状态就是我们制作好了枝叶,但是还没有将这个枝叶挂到这颗树上,因此不能在这里操作Dom模板:document.getElementById("myid")

    //beforeMount() 挂载前,视图view未被真正进行创建,只是进行占位;data模型被创建
    //在这里可以在渲染前最后一次更改数据的机会,不会触发其他的钩子函数,一般可以在这里做初始数据的获取

  },
  mounted() {
    //比较常用的钩子
    //组件挂载完成后执行的函数(页面开始渲染后执行,mounted只会执行一次,可用来实现页面初始化)
    //注释:我们制作好了枝叶,而且已经将枝叶挂到这颗树上,所以此时可以在这里操作Dom模板

    //mounted() 挂载后,视图view被真正创建并进行数据绑定;data模型被创建

    //this.$nextTick监听函数的作用是:在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM。(通俗理解为:某个状态改变立即更新)
    this.name = "张三";
    this.$nextTick(() => {
      //这个this.$nextTick其实是在updated钩子函数之后才会被执行。
      //应用场景是:假设我有一个name只要这个name改变,比如说改变为张三,那么此时我们就需要new一个名字为张三的人。当name改变为李四了,那么就需要创建一个名字为李四的人
      //那有人就说了为什么这个动作不放到updated钩子函数中去做呢?我们之所以这么做是考虑的性能问题,因为这个组件其他的任何一个状态改变都会走updated钩子函数,比如age发生改变,email发生改变。等等都会走updated钩子函数
      //而此时我们的需求是name改变才会new一个人。所以new一个人的动作就放到mounted钩子函数中来处理,不用到放到updated钩子函数中去执行,即便做了更新操作, 生命周期会走到updated钩子函数中,但是钩子函数中没用这个new一个人的动作,所以也不会被执行到,只有这里才会被执行到,提升了性能
    })

    window.onresize = () => { //案例:这里我们做了一个调整窗口大小监听事件(注意:这个事件是一个window事件,它不是在我们页面的Dom中进行绑定的,即便组件卸载了挂载到window的事件还在所以组件卸载前,或者组件卸载后需要手动进行解绑,否则一直在这里执行,会导致内存泄漏的危险)
      console.log(window.innerWidth, window.innerHeight)
    }

  },
  beforeUpdate() {
    //组件更新之前执行的函数(Dom更新前执行)
    //注释:假如一个页面相当于一棵树,组件就是它的枝叶,当前的状态就是以前的树叶掉了,我们重新制作好了新的枝叶,但是还没有将这个新的枝叶挂载到这颗树上,所以此时不能在这里操作Dom模板

    //beforeUpdate()更新前,data模型被更新,view视图未被更新;更新前的准备。只有view上面的数据变化才会触发beforeUpdate和updated,仅属于data中的数据改变是并不能触发。

  },
  updated() {
    //组件更新完毕后执行的函数(Dom更新后执行)
    //注释:我们制作好了新的枝叶,而且已经将新的枝叶挂到这颗树上,所以此时可以在这里操作Dom模板

    //updated()更新后,view和data都被更新,更新完成
  },
  beforeUnmount() {
    //组件卸载之前执行的函数
    //一般可以在此处移除一些时间,window,或者其他的事件监听

    window.onresize = null  //移除window监听事件

  },
  unmounted() {
    //组件卸载完成后执行的函数
  },
  methods: {
    myClick() {
      console.log(123);
    }
  }
}
</script>

VCA模式下的生命周期:Composition API

Vue3 用 setup() 函数替代了 beforeCreate 和 create 钩子。

Vue3 生命周期钩子都以 on+xxx 开头,并且需要手动导入且只能在 setup() 函数内部使用。

setup() 函数是 Vue3 新增的一个选项,它是组合式 API 的统一入口,简单来说,就是所有的 CompositionAPI 新特性都应该写在这个函数内部。

setup函数它接收两个参数:

  1. props:这个还是和 Vue2 使用的组件之间通信的 props一样。
  2. context:定义上下文,这个参数身上有一些我们比较常用的属性,比如
    1. context.emit:等同于 Vue2 的 this.$emit
    2. context.emit:等同于 Vue2 的 this.$emit
    3. context.emit:等同于 Vue2 的 this.$emit
    4. context.expose():当前组件对外暴露属性的函数
<script>
export default {
  setup(props, context) {
    return {}
  }
}
</script>

VCA生命周期 

<template>
  <button @click="myClick">点我</button>
  <div id="myid">
    {{ name }}
  </div>
</template>
<script>
import { ref, nextTick, onBeforeMount, onBeforeUnmount, onBeforeUpdate, onMounted, onUnmounted, onUpdated } from "vue"; //这些方法都是从vue中来的,所以需要导入
export default {
  // setup 是 Vue 3 中一个重要的选项,它用于配置组件的设置和逻辑。setup 函数是 Vue 3 Composition API 的一部分,它用于替代 Vue 2 中的 data、methods、computed 等选项,更好地组织和复用组件的逻辑
  // setup 函数接受两个参数:
  // 参数1:props:包含了组件接收的所有属性的响应式对象
  // 参数2:context:包含了一些组件的上下文信息,如 attrs、slots、emit 等。

  // setup 函数的返回值是一个对象,该对象可以包含以下属性:
  // data:组件的响应式数据。
  // methods:包含组件的方法。
  // computed:计算属性。
  // watch:监听器。
  // 其他自定义属性或函数,以供组件内部使用。
  setup(props, context) {
    const name = ref("张三")

    const myClick = () => {

      name.value = "222";
      nextTick(() => {
        //在VCA中没this无法使用了,需要从vue中进行导入:import {nextTick,ref,...... } from "vue"; //这些方法都是从vue中来的,所以需要导入
        //这个nextTick其实是在onUpdated钩子函数之后才会被执行。
        //应用场景是:假设我有一个name只要这个name改变,比如说改变为张三,那么此时我们就需要new一个名字为张三的人。当name改变为李四了,那么就需要创建一个名字为李四的人
        //那有人就说了为什么这个动作不放到onUpdated钩子函数中去做呢?我们之所以这么做是考虑的性能问题,因为这个组件其他的任何一个状态改变都会走onUpdated钩子函数,比如age发生改变,email发生改变。等等都会走onUpdated钩子函数
        //而此时我们的需求是name改变才会new一个人。所以new一个人的动作就放到onMounted钩子函数中来处理,不用到放到onUpdated钩子函数中去执行,即便做了更新操作, 生命周期会走到onUpdated钩子函数中,但是钩子函数中没用这个new一个人的动作,所以也不会被执行到,只有这里才会被执行到,提升了性能

      })
    }

    onBeforeMount(() => {
      //组件挂载到节点之前执行的函数,此时无法用document.getElementById,因为此时组件还未挂载到节点
      //注释:假如一个页面相当于一棵树,组件就是它的枝叶,当前的状态就是我们制作好了枝叶,但是还没有将这个枝叶挂到这颗树上,因此不能在这里操作Dom模板:document.getElementById("myid")

      //onBeforeMount 挂载前,视图view未被真正进行创建,只是进行占位;data模型被创建
      //在这里可以在渲染前最后一次更改数据的机会,不会触发其他的钩子函数,一般可以在这里做初始数据的获取
    })
    var clearId;
    onMounted(() => {
      //比较常用的钩子
      //组件挂载完成后执行的函数(页面开始渲染后执行,mounted只会执行一次,可用来实现页面初始化)
      //注释:我们制作好了枝叶,而且已经将枝叶挂到这颗树上,所以此时可以在这里操作Dom模板

      //onMounted 挂载后,视图view被真正创建并进行数据绑定;data模型被创建

      //nextTick监听函数的作用是:在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM。(通俗理解为:某个状态改变立即更新)

      //定时器
      clearId = setInterval(() => {
        console.log(123)
      }, 1000);

      //window窗体大小调整监听事件
      window.onresize = () => { //案例:这里我们做了一个调整窗口大小监听事件(注意:这个事件是一个window事件,它不是在我们页面的Dom中进行绑定的,即便组件卸载了挂载到window的事件还在所以组件卸载前,或者组件卸载后需要手动进行解绑,否则一直在这里执行,会导致内存泄漏的危险)
        console.log(window.innerWidth, window.innerHeight)
      }

    })
    onBeforeUpdate(() => {
      //组件更新之前执行的函数(Dom更新前执行)
      //注释:假如一个页面相当于一棵树,组件就是它的枝叶,当前的状态就是以前的树叶掉了,我们重新制作好了新的枝叶,但是还没有将这个新的枝叶挂载到这颗树上,所以此时不能在这里操作Dom模板

      //onBeforeUpdate更新前,data模型被更新,view视图未被更新;更新前的准备。只有view上面的数据变化才会触发beforeUpdate和updated,仅属于data中的数据改变是并不能触发。
    })

    onUpdated(() => {
      //组件更新完毕后执行的函数(Dom更新后执行)
      //注释:我们制作好了新的枝叶,而且已经将新的枝叶挂到这颗树上,所以此时可以在这里操作Dom模板

    })

    onBeforeUnmount(() => {
      //组件卸载之前执行的函数
      //一般可以在此处移除一些定时器,window事件或者其他的事件监听 :如下:
      clearInterval(clearId)//移除定时器
      window.onresize=null; //移除window窗体大小调整监听事件
    })

    onUnmounted(() => {
      //组件卸载之前执行的函数
      //一般可以在此处移除一些时间,window,或者其他的事件监听
    })
    return {
      name,
      myClick
    }
  }
}

</script>