学习生命周期对于一个框架是十分重要的.理解和深入掌握对于在学习和项目中实践有极大的帮助.
vue的生命周期其实跟react的生命周期有很多相似的地方.Vue的生命周期在官网中也命名为生命周期勾子.类似useEffect(16.8 以前是componentDidMounted + componentDidUpdated) 可以理解为vue中onMounted() + onUpdated.
注:引用介绍的部分都是官网的介绍
Vue 生命周期图示
引用官网的一个组件的的生命周期的过程
onBeforeMount
注册一个钩子,在组件被挂载之前被调用。
当这个钩子被调用时,组件已经完成了其响应式状态的设置,但还没有创建 DOM 节点。它即将首次执行 DOM 渲染过程。
这个钩子在服务器端渲染期间不会被调用
onMounted()
注册一个回调函数,在组件挂载完成后执行。
组件在以下情况下被视为已挂载:
其所有同步子组件都已经被挂载 (不包含异步组件或
<Suspense>
树内的组件)。其自身的 DOM 树已经创建完成并插入了父容器中。注意仅当根容器在文档中时,才可以保证组件 DOM 树也在文档中。
这个钩子通常用于执行需要访问组件所渲染的 DOM 树相关的副作用,或是在服务端渲染应用中用于确保 DOM 相关代码仅在客户端执行。
这个钩子在服务器端渲染期间不会被调用。
上面两个生命周期函数想必大家理解的和写的最多生命周期函数了. 在页面初始化,获取异步数据更改状态都会用到
onBeforeUpdate
注册一个钩子,在组件即将因为响应式状态变更而更新其 DOM 树之前调用。
这个钩子可以用来在 Vue 更新 DOM 之前访问 DOM 状态。在这个钩子中更改状态也是安全的。
这个钩子在服务器端渲染期间不会被调用。
onUpdated
注册一个回调函数,在组件因为响应式状态变更而更新其 DOM 树之后调用。
父组件的更新钩子将在其子组件的更新钩子之后调用。
这个钩子会在组件的任意 DOM 更新后被调用,这些更新可能是由不同的状态变更导致的,因为多个状态变更可以在同一个渲染周期中批量执行 (考虑到性能因素)。如果你需要在某个特定的状态更改后访问更新后的 DOM,请使用 nextTick() 作为替代。
注意:不要在这个函数中更改状态,因为会导致死循环.更多的做一些访问更改后的值的操作即可.
例子方便理解
const addNum = async () => {
count.value++
console.log(document.getElementById("btn-toggle")?.textContent)
await nextTick()
console.log(document.getElementById("btn-toggle")?.textContent)
}
添加一个异步操作的按钮,点击后count的值会+1, 但是update并没有执行更新,此时执行的时机是在onBeforeUpdate之前
经过nextTick 函数后执行onUpdate 然后+1
nextTIck是其实是借鉴nodejsd的核心事件循环(event loop).对于了解nodeJs V8核心的很好理解这个生命周期的执行时机和作用.后面会写一个nodejs 时间循环机制和v8的介绍.
onUnmounted
注册一个回调函数,在组件实例被卸载之后调用。
一个组件在以下情况下被视为已卸载:
其所有子组件都已经被卸载。
所有相关的响应式作用 (渲染作用以及
setup()
时创建的计算属性和侦听器) 都已经停止。可以在这个钩子中手动清理一些副作用,例如计时器、DOM 事件监听器或者与服务器的连接。
这个钩子在服务器端渲染期间不会被调用。
例子: 这个官网的例子很不好理解. 手写一个简单清晰的.首先创建两个组件A, B. A是B的父组件
A.vue
<script setup lang="ts">
import B from './B.vue'
import {ref} from 'vue'
defineProps<Props>();
const showChild = ref(true)
const removeChildComponent = () => {
showChild.value = !showChild.value
}
</script>
<template>
<button @click="removeChildComponent" >Remove Child Component</button>
<B v-if="showChild" </Com>
</template>
当点击Remove按钮的时候会将B(子)组件卸载掉,从而触发B组件的onUnmounted勾子函数,打印日志结果.
B.vue组件
<script setup lang="ts">
import {onUnmounted} from 'vue'
onUnmounted(() =>{
console.log("onUnmounted is start")
})
</script>
onErrorCaptured
注册一个钩子,在捕获了后代组件传递的错误时调用。
错误可以从以下几个来源中捕获:
- 组件渲染
- 事件处理器
- 生命周期钩子
setup()
函数- 侦听器
- 自定义指令钩子
- 过渡钩子
例子:
还是父子组件的关系
Parent.vue
<template>
<div>
<h1>Parent Component</h1>
<ChildComponent />
<p v-if="errorMessage">Error: {{ errorMessage }}</p>
</div>
</template>
<script setup lang="ts">
import { ref, onErrorCaptured } from 'vue';
import ChildComponent from './ChildComponent.vue';
const errorMessage = ref<string | null>(null);
onErrorCaptured((err, instance, info) => {
console.error('Error captured:', err);
console.error('Error info:', info);
errorMessage.value = (err as Error).message;
return false; // Prevent the error from propagating further
});
</script>
ChildComponent.vue
<template> <div> <p>This is the child component.</p> </div> </template> <script setup lang="ts"> import { onMounted } from 'vue'; onMounted(() => { throw new Error('An error occurred in ChildComponent'); }); </script>
运行后结果
onRenderTracked
注册一个调试钩子,当组件渲染过程中追踪到响应式依赖时调用。
这个钩子仅在开发模式下可用,且在服务器端渲染期间不会被调用。
onRenderTriggered
注册一个调试钩子,当响应式依赖的变更触发了组件渲染时调用。
这个钩子仅在开发模式下可用,且在服务器端渲染期间不会被调用
onRenderTracked 和onRenderTriggered 结合起来看,这2个hook比较有意思,看官网介绍你根本看不懂说的什么,虽然在生命周期的图示里没有看到但是我们可以写个代码大概看下执行的时机
这个生命周期函数写在子组件内
onBeforeMount(() => {
console.log("onBeforeMount is start");
})
onMounted(() => {
console.log("onMounted is start")
})
onRenderTracked((e) => {
console.log("onRenderTracked is start")
})
onRenderTriggered((e) => {
console.log(" onRenderTriggered is start ")
})
运行下
页面首次初始化的发现onRenderTriggered 并没有执行,而Tracked 函数会在onMounted之前先执行,这也就是说我们可以在dom插入之前截取一些数据或者状态值,进行一些需求的逻辑
而当我们修改父组件的input输入框添加一个数组2时触发响应式调用结果换做在onUpdate之前执行 而onRenderTriggered 没有触发
修改代码我们添加一个异步的按钮在子组件并且使用NextTick显示不同阶段的值
const count = ref(0)
const addNum = async () => {
count.value++
console.log(document.getElementById("btn-toggle")?.textContent)
await nextTick()
console.log(document.getElementById("btn-toggle")?.textContent)
}
<template >
<button id="btn-toggle" @click="addNum" >{{ count }}</button>
</template>
点击按钮后显示结论 当onRenderTriggered 只有在自身组件调用的响应式关联变更的时候(例如ref变化) 才能触发,而onRenderTracked 在深,浅变更的时候都会被触发,也可以说任何响应关系变更都可以触发.简单说父组件的变更可以触发到onRenderTracked,而onRenderTriggered 只关联自身组件的变化.
onActivated
注册一个回调函数,若组件实例是 <KeepAlive> 缓存树的一部分,当组件被插入到 DOM 中时调用。
onDeactivated
注册一个回调函数,若组件实例是 <KeepAlive> 缓存树的一部分,当组件从 DOM 中被移除时调用。
简单的说这个生命周期函数时在keep-alive 这个组件封装下的组件变化的时候会生效
代替正常我们一个组件的 onMounted和onUnmounted的执行
这里简单提一嘴keepAlive(缓存实例). 当一个组件在dom上被移除的时候如果是在keepalive里缓存的。它将被视为组件树的一部分被标记为不活跃而不是卸载.这就是为什么我上面提到是替换onUnmounted.
onServerPrefetch
注册一个异步函数,在组件实例在服务器上被渲染之前调用。
这个之后会单独写一篇关于服务器渲染的文章介绍下,先留个悬念.