📚 课程概述
生命周期是Vue.js和UniApp的核心概念之一,它定义了组件从创建到销毁的整个过程。本课程将深入讲解UniApp的生命周期,包括组件生命周期、页面生命周期、应用生命周期等,帮助你更好地管理组件状态和资源。
学习目标:
- 理解Vue.js组件生命周期的完整流程
- 掌握UniApp页面生命周期的特殊钩子
- 了解应用生命周期的使用场景
- 学会在合适的时机执行初始化和清理操作
- 掌握生命周期钩子的最佳实践
🔄 什么是生命周期?
生命周期的概念
生命周期是指一个组件从创建到销毁的整个过程。Vue.js为这个过程提供了多个钩子函数,允许我们在特定的时机执行代码。
生命周期的作用:
初始化数据 → 渲染视图 → 响应变化 → 清理资源
为什么需要生命周期?
- 初始化:在组件创建时设置初始状态
- 数据获取:在组件挂载后请求数据
- 资源清理:在组件销毁前清理定时器和事件监听
- 性能优化:在合适的时机执行操作以提升性能
🧩 组件生命周期
组件生命周期流程
beforeCreate
→
created
→
beforeMount
→
mounted
→
beforeUpdate
→
updated
→
beforeDestroy
→
destroyed
创建阶段
export default {
// 1. beforeCreate
// 实例刚在内存中被创建出来,组件的props、data、methods都还没有初始化
beforeCreate() {
console.log('beforeCreate: 实例初始化之后')
console.log('data:', this.data)
console.log('methods:', this.methods)
},
// 2. created
// 实例已经在内存中创建完成,data、methods都已初始化
// 但还没有开始编译模板,页面还不能看到内容
created() {
console.log('created: 实例创建完成')
console.log('data:', this.data)
console.log('methods:', this.methods)
// 常用场景:调用接口获取初始数据
this.fetchData()
},
methods: {
fetchData() {
// 获取数据
}
}
}
挂载阶段
export default {
// 3. beforeMount
// 模板编译完成,虚拟DOM已经生成,但还没有挂载到真实DOM上
beforeMount() {
console.log('beforeMount: 挂载开始之前')
console.log('DOM还未渲染')
},
// 4. mounted
// 真实DOM已经挂载完成,页面可以看到渲染的内容
mounted() {
console.log('mounted: 挂载完成')
console.log('DOM已渲染')
// 常用场景:
// 1. 操作DOM
// 2. 使用第三方库(如ECharts、Swiper等)
// 3. 添加事件监听
// 4. 启动定时器
this.initChart()
this.startTimer()
},
methods: {
initChart() {
// 初始化图表
},
startTimer() {
// 启动定时器
this.timer = setInterval(() => {
console.log('定时器执行')
}, 1000)
}
},
beforeDestroy() {
// 清理定时器
if (this.timer) {
clearInterval(this.timer)
}
}
}
更新阶段
export default {
data() {
return {
message: 'Hello'
}
},
// 5. beforeUpdate
// 数据发生变化,但DOM还没有更新
beforeUpdate() {
console.log('beforeUpdate: 数据更新时')
console.log('旧数据:', this.message)
},
// 6. updated
// 数据变化,DOM已经更新完成
updated() {
console.log('updated: 数据更新完成')
console.log('新数据:', this.message)
// 注意:避免在此钩子中修改数据,可能导致无限循环
},
methods: {
updateMessage() {
this.message = 'Hello World'
}
}
}
销毁阶段
export default {
data() {
return {
timer: null,
eventHandler: null
}
},
// 7. beforeDestroy
// 实例销毁之前调用,此时实例仍然完全可用
beforeDestroy() {
console.log('beforeDestroy: 实例销毁之前')
// 常用场景:清理工作
// 1. 清除定时器
// 2. 移除事件监听
// 3. 取消未完成的网络请求
// 4. 销毁第三方库实例
if (this.timer) {
clearInterval(this.timer)
this.timer = null
}
if (this.eventHandler) {
window.removeEventListener('scroll', this.eventHandler)
}
},
// 8. destroyed
// 实例销毁完成,所有的事件监听器和子实例都被移除
destroyed() {
console.log('destroyed: 实例销毁完成')
console.log('实例已完全销毁')
}
}
组件生命周期对比
| 钩子 |
阶段 |
特点 |
常用场景 |
| beforeCreate |
创建 |
实例初始化,data/methods未初始化 |
极少使用 |
| created |
创建 |
data/methods已初始化,DOM未渲染 |
调用API获取数据 |
| beforeMount |
挂载 |
虚拟DOM已生成,真实DOM未挂载 |
最后一次修改数据的机会 |
| mounted |
挂载 |
真实DOM已挂载 |
操作DOM、初始化第三方库 |
| beforeUpdate |
更新 |
数据变化,DOM未更新 |
进一步更改状态 |
| updated |
更新 |
DOM已更新 |
避免修改数据 |
| beforeDestroy |
销毁 |
实例仍可用 |
清理定时器、事件监听 |
| destroyed |
销毁 |
实例已销毁 |
清理工作已完成 |
📱 页面生命周期
页面生命周期钩子
UniApp页面除了组件生命周期外,还有特殊的页面生命周期钩子:
export default {
// 页面加载
// 页面加载时触发,一个页面只会调用一次
onLoad(options) {
console.log('onLoad: 页面加载', options)
// options包含页面跳转传递的参数
// 例如:uni.navigateTo({ url: '/pages/detail/detail?id=1' })
// options = { id: '1' }
// 常用场景:
// 1. 获取页面参数
// 2. 初始化页面数据
// 3. 调用接口获取数据
this.id = options.id
this.fetchDetail(options.id)
},
// 页面显示
// 页面每次显示时都会触发
onShow() {
console.log('onShow: 页面显示')
// 常用场景:
// 1. 刷新页面数据
// 2. 恢复页面状态
// 3. 更新用户信息
this.refreshData()
},
// 页面初次渲染完成
// 页面第一次渲染完成时触发,一个页面只会调用一次
onReady() {
console.log('onReady: 页面初次渲染完成')
// 常用场景:
// 1. 获取节点信息
// 2. 初始化页面组件
this.initPageComponents()
},
// 页面隐藏
// 页面隐藏时触发
onHide() {
console.log('onHide: 页面隐藏')
// 常用场景:
// 1. 暂停定时器
// 2. 保存页面状态
this.pauseTimer()
},
// 页面卸载
// 页面卸载时触发
onUnload() {
console.log('onUnload: 页面卸载')
// 常用场景:
// 1. 清理定时器
// 2. 清理事件监听
// 3. 保存页面数据
this.cleanup()
},
// 下拉刷新
onPullDownRefresh() {
console.log('onPullDownRefresh: 下拉刷新')
// 常用场景:
// 1. 重新加载数据
// 2. 刷新页面内容
this.refreshData()
// 停止下拉刷新动画
uni.stopPullDownRefresh()
},
// 上拉触底
onReachBottom() {
console.log('onReachBottom: 上拉触底')
// 常用场景:
// 1. 加载更多数据
// 2. 分页加载
this.loadMore()
},
// 页面滚动
onPageScroll(e) {
console.log('onPageScroll: 页面滚动', e.scrollTop)
// 常用场景:
// 1. 监听滚动位置
// 2. 实现吸顶效果
// 3. 懒加载图片
this.handleScroll(e.scrollTop)
},
// 分享
onShareAppMessage() {
console.log('onShareAppMessage: 用户点击分享')
return {
title: '分享标题',
path: '/pages/index/index',
imageUrl: '/static/share.png'
}
},
methods: {
fetchDetail(id) {
// 获取详情数据
},
refreshData() {
// 刷新数据
},
initPageComponents() {
// 初始化页面组件
},
pauseTimer() {
// 暂停定时器
},
cleanup() {
// 清理资源
},
loadMore() {
// 加载更多
},
handleScroll(scrollTop) {
// 处理滚动
}
}
}
页面生命周期对比
| 钩子 |
触发时机 |
特点 |
常用场景 |
| onLoad |
页面加载 |
只触发一次,可获取参数 |
获取参数、初始化数据 |
| onShow |
页面显示 |
每次显示都触发 |
刷新数据、恢复状态 |
| onReady |
渲染完成 |
只触发一次,DOM已渲染 |
获取节点信息 |
| onHide |
页面隐藏 |
每次隐藏都触发 |
暂停定时器 |
| onUnload |
页面卸载 |
只触发一次 |
清理资源 |
| onPullDownRefresh |
下拉刷新 |
需要配置开启 |
刷新数据 |
| onReachBottom |
上拉触底 |
滚动到底部触发 |
加载更多 |
🏠 应用生命周期
App.vue中的生命周期
应用生命周期在App.vue中定义,用于管理整个应用的生命周期:
🔄 生命周期执行顺序
页面跳转时的生命周期
从页面A跳转到页面B:
页面A.onHide() → 页面B.onLoad() → 页面B.onShow() → 页面B.onReady()
组件嵌套时的生命周期
父组件和子组件的生命周期:
父beforeCreate → 父created → 父beforeMount → 子beforeCreate → 子created → 子beforeMount → 子mounted → 父mounted
完整的生命周期流程
// 页面首次加载
App.onLaunch()
→ App.onShow()
→ 页面.onLoad()
→ 页面.onShow()
→ 页面.onReady()
→ 组件.beforeCreate()
→ 组件.created()
→ 组件.beforeMount()
→ 组件.mounted()
// 页面跳转
当前页面.onHide()
→ 新页面.onLoad()
→ 新页面.onShow()
→ 新页面.onReady()
// 返回上一页
当前页面.onUnload()
→ 上一页.onShow()
// 应用切换
App.onHide() // 切换到后台
→ App.onShow() // 切换回前台
💡 生命周期最佳实践
使用建议:
- 在created中调用API获取数据,避免在mounted中调用
- 在mounted中操作DOM,初始化第三方库
- 在beforeDestroy中清理定时器、事件监听等资源
- 避免在updated中修改数据,防止无限循环
- 使用onShow刷新数据,而不是每次都重新加载
常见问题:
- 数据不更新:确保在created或mounted中获取数据
- 内存泄漏:及时清理定时器和事件监听
- DOM操作失败:确保在mounted之后操作DOM
- 无限循环:避免在updated中修改数据
🎯 实战案例:用户信息页面
加载中...
{{ user.name }}
{{ user.bio }}
粉丝:{{ user.followers }}
关注:{{ user.following }}
获赞:{{ user.likes }}
用户不存在
🔮 下一步学习方向
掌握了生命周期后,下一课我们将深入学习网络请求,了解如何与后端API进行数据交互。