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函数它接收两个参数:
-
props
:这个还是和Vue2
使用的组件之间通信的props
一样。 -
context
:定义上下文,这个参数身上有一些我们比较常用的属性,比如-
context.emit
:等同于Vue2
的this.$emit
-
context.emit
:等同于Vue2
的this.$emit
-
context.emit
:等同于Vue2
的this.$emit
-
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>