Vue 3 生命周期钩子在 setup 中的注册与执行机制
各位同学,大家好。今天我们来深入探讨 Vue 3 中生命周期钩子在 setup 函数上下文中的注册与执行机制。Vue 3 对比 Vue 2 最大的改变之一就是组件的组织方式,setup 函数的引入使得组件的逻辑复用和代码组织更加灵活。而生命周期钩子,作为组件生命周期中关键节点的切入点,也在 setup 函数中得到了新的使用方式。
Vue 2 生命周期钩子回顾
在深入 Vue 3 之前,我们先简单回顾一下 Vue 2 的生命周期钩子。Vue 2 的生命周期钩子是通过选项对象(Options API)定义的,例如:
export default {
data() {
return {
message: 'Hello Vue 2'
}
},
beforeCreate() {
console.log('beforeCreate');
},
created() {
console.log('created');
},
beforeMount() {
console.log('beforeMount');
},
mounted() {
console.log('mounted');
},
beforeUpdate() {
console.log('beforeUpdate');
},
updated() {
console.log('updated');
},
beforeDestroy() {
console.log('beforeDestroy');
},
destroyed() {
console.log('destroyed');
},
activated() {
console.log('activated');
},
deactivated() {
console.log('deactivated');
},
errorCaptured() {
console.log('errorCaptured');
}
}
这些钩子函数会在组件生命周期的不同阶段被自动调用。这种方式简单直观,但也存在一些问题,比如组件逻辑分散在不同的钩子函数中,不利于代码的组织和复用。
Vue 3 的 Composition API 和 setup 函数
Vue 3 引入了 Composition API,旨在解决 Options API 的一些问题。其中,setup 函数是 Composition API 的核心,它是一个组件选项,用于组织组件的逻辑。所有在 setup 函数中定义的变量和方法,都需要通过 return 语句返回,才能在模板中使用。
<template>
<div>
<p>{{ message }}</p>
</div>
</template>
<script>
import { ref } from 'vue';
export default {
setup() {
const message = ref('Hello Vue 3');
return {
message
}
}
}
</script>
在这个例子中,message 是一个响应式变量,通过 ref 函数创建。在 setup 函数中定义了 message,并通过 return 语句返回,使得模板可以访问并渲染 message 的值。
Vue 3 生命周期钩子在 setup 中的注册方式
在 Vue 3 中,生命周期钩子不再作为选项对象的一部分,而是通过特定的函数进行注册。这些函数都以 on 开头,例如 onMounted、onUpdated、onUnmounted 等。它们都必须在 setup 函数内部调用,才能生效。
以下表格列出了 Vue 3 中常用的生命周期钩子及其对应的注册函数:
| Vue 2 生命周期钩子 | Vue 3 setup 中的注册函数 |
说明 |
|---|---|---|
beforeCreate |
不再使用 | setup 函数本身可以替代 beforeCreate 的作用 |
created |
不再使用 | setup 函数本身可以替代 created 的作用 |
beforeMount |
onBeforeMount |
在组件挂载之前调用 |
mounted |
onMounted |
在组件挂载之后调用 |
beforeUpdate |
onBeforeUpdate |
在组件更新之前调用 |
updated |
onUpdated |
在组件更新之后调用 |
beforeUnmount |
onBeforeUnmount |
在组件卸载之前调用(Vue 3 中 beforeDestroy 更名为 beforeUnmount) |
unmounted |
onUnmounted |
在组件卸载之后调用(Vue 3 中 destroyed 更名为 unmounted) |
activated |
onActivated |
在 keep-alive 组件激活时调用 |
deactivated |
onDeactivated |
在 keep-alive 组件停用时调用 |
errorCaptured |
onErrorCaptured |
当捕获到一个来自子孙组件的错误时被调用 |
renderTracked |
onRenderTracked |
组件渲染追踪依赖时调用,仅在开发环境有效 |
renderTriggered |
onRenderTriggered |
组件渲染被触发时调用,仅在开发环境有效 |
注意: beforeCreate 和 created 钩子在 Vue 3 中不再直接使用,因为 setup 函数本身就承担了它们的作用。在 setup 函数中定义的逻辑,会在组件实例创建之前执行,类似于 beforeCreate 和 created。
以下是一个在 setup 函数中使用 onMounted 钩子的例子:
<template>
<div>
<p>{{ message }}</p>
</div>
</template>
<script>
import { ref, onMounted } from 'vue';
export default {
setup() {
const message = ref('Hello Vue 3');
onMounted(() => {
console.log('Component mounted!');
// 在组件挂载后执行一些操作,例如获取 DOM 元素
console.log(document.querySelector('p').textContent);
});
return {
message
}
}
}
</script>
在这个例子中,onMounted 钩子函数会在组件挂载到 DOM 后执行。可以在这个钩子函数中执行一些需要访问 DOM 元素的操作。
生命周期钩子的执行顺序
Vue 3 生命周期钩子的执行顺序与 Vue 2 类似,但由于 setup 函数的引入,需要考虑 setup 函数本身的影响。
以下是 Vue 3 组件生命周期钩子的执行顺序:
setup函数执行: 这是组件生命周期的起点。在setup函数中,可以定义响应式数据、注册生命周期钩子、以及执行其他初始化逻辑。onBeforeMount: 在组件挂载到 DOM 之前调用。onMounted: 在组件挂载到 DOM 之后调用。onBeforeUpdate: 在组件数据更新之前调用。onUpdated: 在组件数据更新之后调用。onBeforeUnmount: 在组件卸载之前调用。onUnmounted: 在组件卸载之后调用。onActivated: 在keep-alive组件激活时调用。onDeactivated: 在keep-alive组件停用时调用。onErrorCaptured: 当捕获到一个来自子孙组件的错误时被调用。onRenderTracked: 组件渲染追踪依赖时调用,仅在开发环境有效。onRenderTriggered: 组件渲染被触发时调用,仅在开发环境有效。
需要注意的是:
- 如果组件使用了
keep-alive,那么activated和deactivated钩子函数会在组件激活和停用时被调用,而beforeUnmount和unmounted钩子函数只会在组件第一次被卸载时调用。 onErrorCaptured钩子函数用于捕获来自子孙组件的错误。它接收三个参数:错误对象、发生错误的组件实例、以及一个包含错误信息的字符串。onRenderTracked和onRenderTriggered钩子函数仅在开发环境有效,用于调试组件的渲染过程。
生命周期钩子的使用场景
生命周期钩子在 Vue 组件中扮演着重要的角色,它们允许在组件生命周期的不同阶段执行特定的操作。以下是一些常见的生命周期钩子的使用场景:
-
onMounted:- 获取 DOM 元素:在组件挂载到 DOM 后,可以使用
document.querySelector等方法获取 DOM 元素。 - 初始化第三方库:例如,可以使用
onMounted钩子来初始化一个图表库或地图库。 - 发送 HTTP 请求:在组件挂载后,可以发送 HTTP 请求获取数据。
- 获取 DOM 元素:在组件挂载到 DOM 后,可以使用
-
onUpdated:- 更新第三方库:当组件的数据发生变化时,可以使用
onUpdated钩子来更新第三方库的状态。 - 手动更新 DOM:在某些情况下,可能需要手动更新 DOM 元素。
- 更新第三方库:当组件的数据发生变化时,可以使用
-
onBeforeUnmount和onUnmounted:- 清理资源:在组件卸载之前,可以使用
onBeforeUnmount钩子来清理资源,例如取消订阅事件、清除定时器等。 - 释放内存:在组件卸载之后,可以使用
onUnmounted钩子来释放内存,避免内存泄漏。
- 清理资源:在组件卸载之前,可以使用
-
onActivated和onDeactivated:- 缓存数据:在使用
keep-alive组件时,可以使用activated钩子来加载缓存的数据,使用deactivated钩子来缓存数据。
- 缓存数据:在使用
-
onErrorCaptured:- 处理错误:可以使用
onErrorCaptured钩子来捕获来自子孙组件的错误,并进行处理,例如记录错误日志、显示错误提示等。
- 处理错误:可以使用
示例:使用 onMounted 发送 HTTP 请求
以下是一个使用 onMounted 钩子发送 HTTP 请求的例子:
<template>
<div>
<p v-if="loading">Loading...</p>
<p v-else>{{ data }}</p>
</div>
</template>
<script>
import { ref, onMounted } from 'vue';
export default {
setup() {
const data = ref(null);
const loading = ref(true);
onMounted(async () => {
try {
const response = await fetch('https://jsonplaceholder.typicode.com/todos/1');
const json = await response.json();
data.value = json;
} catch (error) {
console.error('Error fetching data:', error);
data.value = 'Error fetching data';
} finally {
loading.value = false;
}
});
return {
data,
loading
}
}
}
</script>
在这个例子中,onMounted 钩子函数会在组件挂载到 DOM 后执行。在这个钩子函数中,使用 fetch API 发送 HTTP 请求,获取数据。在获取数据成功后,将数据赋值给 data 变量,并将 loading 变量设置为 false。如果获取数据失败,则将 data 变量设置为错误信息,并将 loading 变量设置为 false。
示例:使用 onBeforeUnmount 清理资源
以下是一个使用 onBeforeUnmount 钩子清理资源的例子:
<template>
<div>
<p>{{ message }}</p>
</div>
</template>
<script>
import { ref, onMounted, onBeforeUnmount } from 'vue';
export default {
setup() {
const message = ref('Hello Vue 3');
let timer = null;
onMounted(() => {
// 设置一个定时器
timer = setInterval(() => {
console.log('Timer running...');
}, 1000);
});
onBeforeUnmount(() => {
// 在组件卸载之前,清除定时器
clearInterval(timer);
timer = null;
console.log('Timer cleared!');
});
return {
message
}
}
}
</script>
在这个例子中,onMounted 钩子函数会在组件挂载到 DOM 后执行。在这个钩子函数中,设置一个定时器,每隔 1 秒钟打印一条信息。onBeforeUnmount 钩子函数会在组件卸载之前执行。在这个钩子函数中,清除定时器,避免内存泄漏。
onErrorCaptured 的使用
onErrorCaptured 钩子可以用来捕获来自子孙组件的错误,进行统一处理,防止错误扩散。
<template>
<div>
<ChildComponent />
</div>
</template>
<script>
import { onErrorCaptured } from 'vue';
import ChildComponent from './ChildComponent.vue';
export default {
components: {
ChildComponent
},
setup() {
onErrorCaptured((err, instance, info) => {
console.error('Error captured:', err);
console.log('Component instance:', instance);
console.log('Error info:', info);
// 可以发送错误到服务器,或者显示一个友好的错误提示
return false; // 如果返回 false,错误将继续向上冒泡
});
return {};
}
}
</script>
ChildComponent.vue:
<template>
<div>
<button @click="throwError">Throw Error</button>
</div>
</template>
<script>
export default {
setup() {
const throwError = () => {
throw new Error('This is an error from ChildComponent');
};
return {
throwError
}
}
}
</script>
在这个例子中,如果 ChildComponent 组件抛出一个错误,onErrorCaptured 钩子会被调用,错误信息会被打印到控制台。onErrorCaptured 可以接收三个参数:err (错误对象), instance (发生错误的组件实例), info (错误的来源信息)。 返回 false 可以阻止错误继续向上冒泡,否则错误会被传递到更上层的 onErrorCaptured 或者全局的错误处理函数。
总结:使用Composition API,生命周期管理更灵活
总而言之,Vue 3 中生命周期钩子的注册与执行机制与 Vue 2 相比有了很大的变化。通过 setup 函数和以 on 开头的注册函数,可以更加灵活地组织组件的逻辑,提高代码的可读性和可维护性。理解这些机制对于开发高质量的 Vue 3 应用至关重要。利用Composition API,我们可以更好地管理组件的生命周期,从而构建更健壮和高效的应用程序。
更多IT精英技术系列讲座,到智猿学院