当我们运行vue项目,看到了屏幕上显示的界面,看到了界面上显示的数据和标签,之后将这个界面叉掉,这一过程其实经历了一整个vue的生命周期的四个阶段,即创建阶段、挂载阶段、更新阶段以及销毁阶段, 而对于每个阶段的启动前和完成后,都可以使用一对钩子来访问。
一、创建阶段 (beforeCreate/created两钩子)
- beforeCreate:在实例初始化之后,数据观测 和 事件/侦听器的配置之前被调用。此时,组件的
data
、computed
、watch
、methods
上的方法和数据都不可访问,例如我提前在data中定义了一个变量,在该阶段访问它,只会获得空值。 - created:在实例创建完成后被立即调用。在这一步,实例已完成以下的配置:数据观测,属性和方法的运算,
watch/event
事件回调。然而,挂载阶段还没开始,$el
属性目前不可见。
<template>
<div>{{ message }}</div>
</template>
<script>
export default {
data() {
return {
message: 'Hello Vue!'
}
},
beforeCreate() {
console.log('beforeCreate:', this.message); // undefined,因为data还未初始化
// 这里可以初始化一些不需要依赖data或methods的属性
},
created() {
console.log('created:', this.message); // Hello Vue!,此时data已初始化
// 这里可以调用API,获取数据等
this.fetchData();
},
methods: {
fetchData() {
// 模拟异步获取数据
setTimeout(() => {
// 假设这里是从服务器获取的数据
const newData = 'New message from server';
// 注意:直接修改data属性会触发视图更新
this.message = newData;
}, 1000);
}
}
}
</script>
二、挂载阶段 (beforeMount/mounted)
- beforeMount:在挂载开始之前被调用:相关的 render 函数首次被调用。该钩子在服务器端渲染期间不被调用。
- mounted:
el
被新创建的vm.$el
替换,并挂载到实例上去之后调用该钩子。如果根实例挂载了一个文档内元素,当mounted
被调用时vm.$el
也在文档内。
<template>
<div ref="myDiv">{{ message }}</div>
</template>
<script>
export default {
data() {
return {
message: '存储的内容'
}
},
beforeMount() {
// 这里的$el存在,但内容还是模板字符串
console.log('beforeMount:', this.$el.textContent); // 可能是空字符串或注释
},
mounted() {
// 组件已挂载到DOM上,可以访问到DOM元素
console.log('mounted:', this.$refs.myDiv.textContent); // Hello Vue in DOM!
// 可以在这里操作DOM,如添加事件监听器等
this.$refs.myDiv.addEventListener('click', this.handleClick);
},
methods: {
handleClick() {
console.log('myDiv被点击了');
}
},
beforeDestroy() {
// 清理事件监听器,避免内存泄漏
this.$refs.myDiv.removeEventListener('click', this.handleClick);
}
}
</script>
三、更新阶段(beforeUpdate/updated)
- beforeUpdate:数据更新时调用,发生在虚拟 DOM 打补丁之前。这里适合在更新之前访问现有的 DOM,比如手动移除已添加的事件监听器。
- updated:由于数据更改导致的虚拟 DOM 重新渲染和打补丁,在这之后会调用这个钩子。当这个钩子被调用时,组件 DOM 已经更新,所以现在可以执行依赖于 DOM 的操作。然而在大多数情况下,应该避免在此期间更改状态,因为这可能会导致更新无限循环。(我要更新了,但是更新时发现有个东西要更新,之后更新这个东西,但是又发现这个东西要更新,然后又又更新......无限循环)
<template>
<div>{{ counter }}</div>
<button @click="increment">Increment</button>
</template>
<script>
export default {
data() {
return {
counter: 0
}
},
beforeUpdate() {
console.log('beforeUpdate:', this.counter); // 更新前的值
//此时DOM还未更新
},
updated() {
console.log('updated:', this.counter); // 更新后的值
// 可以在这里执行依赖于DOM的操作,但要避免引起无限更新循环
},
methods: {
increment() {
this.counter++;
}
}
}
</script>
四、销毁阶段(beforeDestroy/destroyed)
在这个阶段,Vue 实例将从 DOM 中卸载,并且清理所有的绑定和事件监听。
-
beforeDestroy(): 在实例销毁之前调用。这时实例仍然完全可用。
-
destroyed(): 在实例销毁后调用。调用之后,Vue 实例上的所有东西都会解除绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。
销毁阶段的代码我已经融入到前三个阶段的代码中,在此不再展示.
整个流程:
beforeCreate -> created -> beforeMount -> mounted -> beforeUpdate -> updated -> beforeDestroy -> destroyed