Vue 2与Vue 3 生命周期钩子的变化与迁移指南
2024-01-167.8k 阅读
Vue 生命周期钩子函数概述
在Vue开发中,生命周期钩子函数是非常重要的概念。它们允许开发者在Vue实例从创建到销毁的不同阶段执行特定的代码逻辑。这些钩子函数为我们提供了对Vue实例在各个阶段的控制权,无论是数据初始化、DOM操作,还是资源清理等,都可以借助生命周期钩子来实现。
在Vue 2中,我们已经熟悉了一系列生命周期钩子,如 beforeCreate
、created
、beforeMount
、mounted
、beforeUpdate
、updated
、beforeDestroy
、destroyed
等。Vue 3在保持部分钩子函数名称和功能一致性的同时,也对生命周期钩子进行了一些改动,以适应新的Composition API和更好的代码组织。
Vue 2生命周期钩子函数详解
beforeCreate
:在实例初始化之后,数据观测 (data observer) 和 event/watcher 事件配置之前被调用。此时,实例的data
和methods
等属性都还未被初始化,所以在这个钩子函数中无法访问到这些属性。
new Vue({
data() {
return {
message: 'Hello'
}
},
beforeCreate() {
console.log(this.message); // undefined
}
});
created
:实例已经创建完成,此时可以访问data
和methods
等属性。通常在这个钩子函数中进行数据的初始化、AJAX请求等操作。
new Vue({
data() {
return {
message: 'Hello',
user: null
}
},
created() {
// 模拟AJAX请求获取用户数据
setTimeout(() => {
this.user = { name: 'John' };
}, 1000);
}
});
beforeMount
:在挂载开始之前被调用,此时$el
已经被创建,但是还没有挂载到DOM中,模板也还没有被渲染。可以在这个钩子函数中对即将挂载的DOM进行一些最后的修改。
<template>
<div id="app">{{ message }}</div>
</template>
<script>
export default {
data() {
return {
message: 'Hello'
}
},
beforeMount() {
console.log(this.$el); // <div id="app"></div>
}
}
</script>
mounted
:实例被挂载后调用,此时$el
已经挂载到DOM中,模板也已经渲染完成。可以在这个钩子函数中进行DOM操作、初始化第三方插件等。
<template>
<div id="app">{{ message }}</div>
</template>
<script>
export default {
data() {
return {
message: 'Hello'
}
},
mounted() {
console.log(this.$el.textContent); // Hello
// 初始化第三方插件,例如Chart.js
const ctx = this.$el.querySelector('canvas');
new Chart(ctx, {
type: 'bar',
data: {
labels: ['Red', 'Blue', 'Yellow'],
datasets: [{
label: '# of Votes',
data: [12, 19, 3],
backgroundColor: [
'rgba(255, 99, 132, 0.2)',
'rgba(54, 162, 235, 0.2)',
'rgba(255, 206, 86, 0.2)'
],
borderColor: [
'rgba(255, 99, 132, 1)',
'rgba(54, 162, 235, 1)',
'rgba(255, 206, 86, 1)'
],
borderWidth: 1
}]
},
options: {
scales: {
yAxes: [{
ticks: {
beginAtZero: true
}
}]
}
}
});
}
}
</script>
beforeUpdate
:数据更新时调用,发生在虚拟DOM重新渲染和打补丁之前。可以在这个钩子函数中对即将更新的数据进行一些预处理操作。
<template>
<div id="app">
<input v-model="message">
<p>{{ message }}</p>
</div>
</template>
<script>
export default {
data() {
return {
message: 'Hello'
}
},
beforeUpdate() {
console.log('数据即将更新,当前值:', this.message);
}
}
</script>
updated
:数据更新后调用,虚拟DOM重新渲染和打补丁完成。此时可以访问更新后的DOM。需要注意的是,在这个钩子函数中如果再次修改数据,可能会导致无限循环更新。
<template>
<div id="app">
<input v-model="message">
<p>{{ message }}</p>
</div>
</template>
<script>
export default {
data() {
return {
message: 'Hello'
}
},
updated() {
console.log('数据已更新,新值:', this.message);
}
}
</script>
beforeDestroy
:实例销毁之前调用,在这一步,实例仍然完全可用。可以在这个钩子函数中进行一些清理操作,如清除定时器、解绑事件监听器等。
<template>
<div id="app">
<button @click="destroy">销毁实例</button>
</div>
</template>
<script>
export default {
data() {
return {
timer: null
}
},
created() {
this.timer = setInterval(() => {
console.log('定时器在运行');
}, 1000);
},
beforeDestroy() {
clearInterval(this.timer);
console.log('实例即将销毁,清除定时器');
},
methods: {
destroy() {
this.$destroy();
}
}
}
</script>
destroyed
:实例销毁后调用,此时所有的指令都被解绑,所有的事件监听器被移除,所有的子实例也都被销毁。
<template>
<div id="app">
<button @click="destroy">销毁实例</button>
</div>
</template>
<script>
export default {
data() {
return {
timer: null
}
},
created() {
this.timer = setInterval(() => {
console.log('定时器在运行');
}, 1000);
},
beforeDestroy() {
clearInterval(this.timer);
console.log('实例即将销毁,清除定时器');
},
destroyed() {
console.log('实例已销毁');
},
methods: {
destroy() {
this.$destroy();
}
}
}
</script>
Vue 3生命周期钩子函数的变化
- 名称变化:Vue 3中部分生命周期钩子函数的名称发生了变化,主要是为了与Composition API更好地结合。例如,
beforeDestroy
改为beforeUnmount
,destroyed
改为unmounted
,beforeUpdate
改为beforeUpdate
(虽然名称未变,但在Composition API中的使用方式有所不同),updated
改为updated
(同样使用方式有变化)。这种变化是为了更准确地描述组件从挂载到卸载过程中的不同阶段,同时也与新的API设计理念保持一致。 - 与Composition API的结合:在Vue 3的Composition API中,生命周期钩子函数的使用方式发生了较大变化。在Vue 2中,生命周期钩子函数是定义在组件的选项对象中的,而在Vue 3的Composition API中,我们使用
setup
函数来定义组件逻辑,生命周期钩子函数通过导入特定的函数来使用。例如,要使用created
钩子函数,在Composition API中我们这样做:
<template>
<div>{{ message }}</div>
</template>
<script setup>
import { ref, onMounted } from 'vue';
const message = ref('Hello');
onMounted(() => {
console.log('组件已挂载');
});
</script>
这里的 onMounted
函数就是Vue 3中 mounted
生命周期钩子函数在Composition API中的使用方式。这种方式使得我们可以更灵活地组织代码,将相关的逻辑组合在一起,而不是像Vue 2那样将所有的生命周期钩子函数都放在组件选项对象中。
Vue 3生命周期钩子函数详解
setup
函数中的生命周期钩子导入:在setup
函数中,我们通过导入相应的函数来使用生命周期钩子。常用的导入函数有onBeforeMount
、onMounted
、onBeforeUpdate
、onUpdated
、onBeforeUnmount
、onUnmounted
等。
<template>
<div id="app">{{ message }}</div>
</template>
<script setup>
import { ref, onBeforeMount, onMounted, onBeforeUpdate, onUpdated, onBeforeUnmount, onUnmounted } from 'vue';
const message = ref('Hello');
onBeforeMount(() => {
console.log('组件即将挂载');
});
onMounted(() => {
console.log('组件已挂载');
});
onBeforeUpdate(() => {
console.log('数据即将更新');
});
onUpdated(() => {
console.log('数据已更新');
});
onBeforeUnmount(() => {
console.log('组件即将卸载');
});
onUnmounted(() => {
console.log('组件已卸载');
});
</script>
onBeforeMount
:在组件挂载到DOM之前调用,类似于Vue 2中的beforeMount
。在这个钩子函数中,可以对即将挂载的组件进行一些初始化操作,如设置一些全局变量、初始化第三方库的配置等。
<template>
<div id="app">{{ message }}</div>
</template>
<script setup>
import { ref, onBeforeMount } from 'vue';
const message = ref('Hello');
onBeforeMount(() => {
// 假设这里初始化一个全局的日志记录器
console.log('初始化日志记录器');
});
</script>
onMounted
:组件挂载到DOM后调用,与Vue 2中的mounted
类似。可以在这个钩子函数中进行DOM操作、初始化需要DOM元素的第三方插件等。
<template>
<div id="app">
<canvas id="myChart"></canvas>
</div>
</template>
<script setup>
import { onMounted } from 'vue';
import Chart from 'chart.js';
onMounted(() => {
const ctx = document.getElementById('myChart').getContext('2d');
new Chart(ctx, {
type: 'line',
data: {
labels: ['January', 'February', 'March', 'April', 'May', 'June', 'July'],
datasets: [{
label: 'My First dataset',
data: [65, 59, 80, 81, 56, 55, 40],
fill: false,
borderColor: 'rgb(75, 192, 192)',
tension: 0.1
}]
}
});
});
</script>
onBeforeUpdate
:在数据更新导致组件重新渲染之前调用,与Vue 2中的beforeUpdate
类似。可以在这个钩子函数中对即将更新的数据进行一些预处理操作,如数据验证、备份旧数据等。
<template>
<div id="app">
<input v-model="message">
<p>{{ message }}</p>
</div>
</template>
<script setup>
import { ref, onBeforeUpdate } from 'vue';
const message = ref('Hello');
onBeforeUpdate(() => {
console.log('数据即将更新,当前值:', message.value);
});
</script>
onUpdated
:在数据更新导致组件重新渲染完成后调用,类似于Vue 2中的updated
。需要注意在这个钩子函数中避免再次修改数据导致无限循环更新。可以在这个钩子函数中进行一些依赖于更新后DOM状态的操作。
<template>
<div id="app">
<input v-model="message">
<p>{{ message }}</p>
</div>
</template>
<script setup>
import { ref, onUpdated } from 'vue';
const message = ref('Hello');
onUpdated(() => {
console.log('数据已更新,新值:', message.value);
});
</script>
onBeforeUnmount
:在组件卸载之前调用,取代了Vue 2中的beforeDestroy
。可以在这个钩子函数中进行一些清理操作,如清除定时器、解绑事件监听器等。
<template>
<div id="app">
<button @click="unmount">卸载组件</button>
</div>
</template>
<script setup>
import { ref, onBeforeUnmount } from 'vue';
const timer = ref(null);
onBeforeUnmount(() => {
if (timer.value) {
clearInterval(timer.value);
console.log('清除定时器');
}
});
const unmount = () => {
// 这里假设通过某种方式卸载组件
console.log('开始卸载组件');
};
</script>
onUnmounted
:在组件卸载完成后调用,取代了Vue 2中的destroyed
。此时组件相关的所有资源都已被清理。
<template>
<div id="app">
<button @click="unmount">卸载组件</button>
</div>
</template>
<script setup>
import { onUnmounted } from 'vue';
onUnmounted(() => {
console.log('组件已卸载');
});
const unmount = () => {
// 这里假设通过某种方式卸载组件
console.log('开始卸载组件');
};
</script>
Vue 2到Vue 3生命周期钩子的迁移指南
- 钩子函数名称替换:首先,需要将Vue 2中的
beforeDestroy
替换为beforeUnmount
,destroyed
替换为unmounted
。这是最基本的名称变化迁移。 - 选项式API到Composition API的迁移:如果使用的是选项式API(即Vue 2中常用的方式),并且想要迁移到Vue 3的Composition API,需要将生命周期钩子函数的定义方式进行改变。例如,将Vue 2中的
created
钩子函数:
export default {
data() {
return {
message: 'Hello'
}
},
created() {
console.log('组件创建完成');
}
}
迁移到Vue 3的Composition API中:
<template>
<div>{{ message }}</div>
</template>
<script setup>
import { ref, onMounted } from 'vue';
const message = ref('Hello');
onMounted(() => {
console.log('组件创建完成');
});
</script>
- 数据访问和作用域:在Vue 2的选项式API中,
this
指向Vue实例,可以直接访问data
和methods
等属性。而在Vue 3的Composition API中,setup
函数中的this
并不指向Vue实例。在setup
函数中定义的数据和函数需要通过ref
、reactive
等函数进行处理,并且在模板中使用时也有不同的方式。例如,在Vue 2中:
<template>
<div>{{ message }}</div>
</template>
<script>
export default {
data() {
return {
message: 'Hello'
}
},
created() {
this.message = 'World';
}
}
</script>
在Vue 3的Composition API中:
<template>
<div>{{ message }}</div>
</template>
<script setup>
import { ref } from 'vue';
const message = ref('Hello');
// 这里不能直接用this.message,需要使用message.value
setTimeout(() => {
message.value = 'World';
}, 1000);
</script>
- 复杂组件逻辑迁移:对于复杂的组件逻辑,在迁移过程中需要注意将相关的生命周期钩子函数逻辑按照Vue 3的方式进行整理。例如,如果在Vue 2中有一个组件,在
mounted
钩子函数中进行了大量的DOM操作和第三方插件初始化,并且还依赖于其他data
属性,在迁移到Vue 3时,需要将这些逻辑合理地分布在setup
函数中的onMounted
钩子函数以及相关的数据定义和处理中。
<!-- Vue 2组件 -->
<template>
<div id="app">
<canvas id="myChart"></canvas>
</div>
</template>
<script>
export default {
data() {
return {
chartData: {
labels: ['January', 'February', 'March', 'April', 'May', 'June', 'July'],
datasets: [{
label: 'My First dataset',
data: [65, 59, 80, 81, 56, 55, 40],
fill: false,
borderColor: 'rgb(75, 192, 192)',
tension: 0.1
}]
}
};
},
mounted() {
const ctx = this.$el.querySelector('#myChart').getContext('2d');
new Chart(ctx, {
type: 'line',
data: this.chartData
});
}
}
</script>
<!-- Vue 3组件 -->
<template>
<div id="app">
<canvas id="myChart"></canvas>
</div>
</template>
<script setup>
import { reactive, onMounted } from 'vue';
import Chart from 'chart.js';
const chartData = reactive({
labels: ['January', 'February', 'March', 'April', 'May', 'June', 'July'],
datasets: [{
label: 'My First dataset',
data: [65, 59, 80, 81, 56, 55, 40],
fill: false,
borderColor: 'rgb(75, 192, 192)',
tension: 0.1
}]
});
onMounted(() => {
const ctx = document.getElementById('myChart').getContext('2d');
new Chart(ctx, {
type: 'line',
data: chartData
});
});
</script>
- 事件监听和解绑:在Vue 2中,我们可能在
created
或mounted
钩子函数中添加事件监听器,并在beforeDestroy
钩子函数中解绑。在Vue 3中,使用onMounted
添加事件监听器,并在onBeforeUnmount
中解绑。
<!-- Vue 2组件 -->
<template>
<div id="app"></div>
</template>
<script>
export default {
created() {
window.addEventListener('resize', this.handleResize);
},
beforeDestroy() {
window.removeEventListener('resize', this.handleResize);
},
methods: {
handleResize() {
console.log('窗口大小改变');
}
}
}
</script>
<!-- Vue 3组件 -->
<template>
<div id="app"></div>
</template>
<script setup>
import { onMounted, onBeforeUnmount } from 'vue';
const handleResize = () => {
console.log('窗口大小改变');
};
onMounted(() => {
window.addEventListener('resize', handleResize);
});
onBeforeUnmount(() => {
window.removeEventListener('resize', handleResize);
});
</script>
通过以上步骤和示例,我们可以较为顺利地将Vue 2中的生命周期钩子函数相关逻辑迁移到Vue 3中,无论是从名称替换、API使用方式改变,还是复杂逻辑的重新组织,都能找到对应的方法,使得我们能够更好地利用Vue 3的新特性进行前端开发。