第4章:响应式系统与生命周期
第11节:生命周期钩子详解
4.11.1 生命周期图示与阶段划分
Vue组件的生命周期可以分为四个主要阶段:
初始化阶段 → 挂载阶段 → 更新阶段 → 卸载阶段生命周期图示
+---------------------------+-------------------------+---------------------------+
| 初始化阶段 | 挂载阶段 | 更新阶段 |
+---------------------------+-------------------------+---------------------------+
| | | |
| beforeCreate | beforeMount | beforeUpdate |
| created | mounted | updated |
| | | |
+---------------------------+-------------------------+---------------------------+
|
↓
+---------------------------+
| 卸载阶段 |
+---------------------------+
| |
| beforeUnmount |
| unmounted |
| |
+---------------------------+4.11.2 每个钩子的具体用途
初始化阶段
beforeCreate
- 组件实例刚被创建,数据观测和事件机制尚未初始化
- 此时无法访问
data、computed、methods等 - 用途:可以做一些初始化前的准备工作,如加载外部资源
created
- 组件实例创建完成,数据观测和事件机制已初始化
- 可以访问
data、computed、methods等 - 但DOM尚未生成,
$el属性不可用 - 用途:适合进行数据请求、初始化数据等操作
挂载阶段
beforeMount
- 模板编译完成,但尚未挂载到DOM
$el属性已存在,但尚未替换真实DOM- 用途:可以修改模板数据,不会触发重渲染
mounted
- 组件已挂载到DOM,
$el属性指向真实DOM - 可以访问和操作DOM元素
- 用途:适合进行DOM操作、第三方库初始化等
- 组件已挂载到DOM,
更新阶段
beforeUpdate
- 数据更新后,DOM更新前触发
- 可以访问更新前的DOM和数据
- 用途:可以在更新前获取DOM状态,如滚动位置
updated
- DOM已更新,可以访问更新后的DOM
- 用途:适合进行DOM操作、第三方库更新等
- 注意:避免在updated中修改数据,可能导致无限循环
卸载阶段
beforeUnmount
- 组件即将卸载,仍可以访问组件实例和DOM
- 用途:适合清除定时器、取消事件监听、清理第三方库等
unmounted
- 组件已卸载,所有事件监听已移除,子组件已销毁
- 用途:可以进行最终的清理工作
错误处理
- errorCaptured
- 捕获子孙组件抛出的错误
- 可以返回false阻止错误继续向上传播
- 用途:适合全局错误处理、错误日志收集等
export default {
// 初始化阶段
beforeCreate() {
// 实例初始化后,数据观测之前
console.log('beforeCreate')
},
created() {
// 实例创建完成,数据观测完成
// 可以访问this,但DOM未生成
console.log('created')
},
// 挂载阶段
beforeMount() {
// 模板编译完成,但未挂载到DOM
console.log('beforeMount')
},
mounted() {
// 实例挂载到DOM
// 可以访问DOM元素
console.log('mounted')
},
// 更新阶段
beforeUpdate() {
// 数据更新,DOM重新渲染之前
console.log('beforeUpdate')
},
updated() {
// 数据更新,DOM重新渲染之后
console.log('updated')
},
// 卸载阶段
beforeUnmount() {
// 实例卸载之前
console.log('beforeUnmount')
},
unmounted() {
// 实例卸载完成
console.log('unmounted')
},
// 错误处理
errorCaptured(err, instance, info) {
// 捕获子孙组件错误
console.error('errorCaptured:', err, instance, info)
}
}在组合式API中使用生命周期钩子
<script setup>
import { onBeforeMount, onMounted, onBeforeUpdate, onUpdated, onBeforeUnmount, onUnmounted, onErrorCaptured } from 'vue'
// 初始化阶段
onMounted(() => {
console.log('组件已挂载')
})
onUpdated(() => {
console.log('组件已更新')
})
onUnmounted(() => {
console.log('组件已卸载')
})
onErrorCaptured((err, instance, info) => {
console.error('捕获到错误:', err, instance, info)
})
</script>| 选项式API | 组合式API |
|---|---|
| beforeCreate | 无直接对应,使用setup() |
| created | 无直接对应,使用setup() |
| beforeMount | onBeforeMount |
| mounted | onMounted |
| beforeUpdate | onBeforeUpdate |
| updated | onUpdated |
| beforeUnmount | onBeforeUnmount |
| unmounted | onUnmounted |
| errorCaptured | onErrorCaptured |
4.11.3 生命周期钩子的实际应用场景
数据请求
export default {
data() {
return {
users: []
}
},
created() {
// 在created中发起数据请求
fetch('/api/users')
.then(response => response.json())
.then(data => {
this.users = data
})
}
}第三方库初始化
export default {
mounted() {
// 在mounted中初始化第三方库
this.$nextTick(() => {
// 确保DOM已完全渲染
new Swiper(this.$refs.swiper, {
// 配置项
})
})
},
beforeUnmount() {
// 在beforeUnmount中销毁第三方库
if (this.swiper) {
this.swiper.destroy()
}
}
}定时器管理
export default {
data() {
return {
count: 0,
timer: null
}
},
mounted() {
// 在mounted中启动定时器
this.timer = setInterval(() => {
this.count++
}, 1000)
},
beforeUnmount() {
// 在beforeUnmount中清除定时器
if (this.timer) {
clearInterval(this.timer)
}
}
}事件监听管理
export default {
mounted() {
// 在mounted中添加事件监听
window.addEventListener('resize', this.handleResize)
},
beforeUnmount() {
// 在beforeUnmount中移除事件监听
window.removeEventListener('resize', this.handleResize)
},
methods: {
handleResize() {
// 处理窗口大小变化
}
}
}4.11.4 异步操作在生命周期中的位置
数据请求
- 推荐在
created中发起数据请求,因为此时组件实例已创建,可以访问data和methods - 也可以在
mounted中发起数据请求,但会稍晚一些 - 避免在
beforeCreate中发起数据请求,因为此时无法访问组件实例
异步DOM操作
- 当需要在数据更新后操作DOM时,使用
$nextTick $nextTick会在DOM更新后执行回调函数
export default {
data() {
return {
message: 'Hello'
}
},
methods: {
updateMessage() {
this.message = 'World'
// DOM尚未更新
console.log(this.$refs.msg.textContent) // Hello
this.$nextTick(() => {
// DOM已更新
console.log(this.$refs.msg.textContent) // World
})
}
}
}异步组件加载
- 使用
defineAsyncComponent创建异步组件 - 可以在组件加载过程中显示加载状态
import { defineAsyncComponent } from 'vue'
const AsyncComponent = defineAsyncComponent({
// 加载组件
loader: () => import('./AsyncComponent.vue'),
// 加载中显示的组件
loadingComponent: LoadingComponent,
// 加载超时时间
timeout: 3000,
// 加载错误显示的组件
errorComponent: ErrorComponent
})总结
Vue组件的生命周期钩子提供了在不同阶段执行代码的机会,通过合理使用这些钩子,可以实现数据请求、DOM操作、第三方库初始化等功能。
在使用生命周期钩子时,需要注意:
- 避免在updated中修改数据,可能导致无限循环
- 及时清理定时器、事件监听等资源,避免内存泄漏
- 异步操作使用$nextTick确保DOM更新完成
- 组合式API提供了更灵活的生命周期钩子使用方式
在下一章中,我们将学习Vue.js的过渡与动画效果。