生命周期详解 - 页面什么时候加载,什么时候销毁?
⏰ 生命周期就像人的一生,有出生、成长、衰老、死亡。页面也是如此!
什么是生命周期?
生命周期就是页面从创建到销毁的整个过程。
想象一下:
- 你打开一个页面 → 页面"出生"了
- 页面显示内容 → 页面"成长"了
- 你切换到其他页面 → 页面"休眠"了
- 你返回这个页面 → 页面"苏醒"了
- 你关闭这个页面 → 页面"死亡"了
在这些关键时刻,我们可以执行特定的代码,这就是生命周期的作用!
应用生命周期
应用生命周期是整个应用的生命周期,在App.vue
中定义:
javascript
// App.vue
export default {
onLaunch: function() {
console.log('应用启动')
// 应用第一次启动时执行
// 适合做:初始化全局数据、检查更新、获取系统信息等
},
onShow: function() {
console.log('应用显示')
// 应用从后台进入前台时执行
// 适合做:刷新数据、重新连接网络等
},
onHide: function() {
console.log('应用隐藏')
// 应用从前台进入后台时执行
// 适合做:保存数据、暂停音乐等
},
onError: function(err) {
console.log('应用错误:', err)
// 应用发生错误时执行
// 适合做:错误上报、用户提示等
}
}
页面生命周期
页面生命周期是单个页面的生命周期,在每个.vue
页面中定义:
1. onLoad - 页面加载
触发时机:页面第一次加载时 执行次数:只执行一次 适合做什么:接收参数、初始化数据、调用API
javascript
export default {
data() {
return {
userInfo: {},
productList: []
}
},
onLoad(options) {
console.log('页面加载,参数:', options)
// 接收页面参数
const userId = options.userId
// 初始化数据
this.initData()
// 调用API
this.getUserInfo(userId)
this.getProductList()
},
methods: {
initData() {
// 初始化页面数据
},
async getUserInfo(userId) {
// 获取用户信息
try {
const res = await uni.request({
url: '/api/user/' + userId
})
this.userInfo = res.data
} catch (err) {
console.error('获取用户信息失败:', err)
}
},
async getProductList() {
// 获取商品列表
}
}
}
2. onShow - 页面显示
触发时机:页面显示时(包括第一次显示和从其他页面返回) 执行次数:可能多次执行 适合做什么:刷新数据、重新获取最新状态
javascript
export default {
onShow() {
console.log('页面显示')
// 刷新购物车数量
this.updateCartCount()
// 检查登录状态
this.checkLoginStatus()
// 刷新页面数据(如果需要)
this.refreshData()
},
methods: {
updateCartCount() {
// 更新购物车数量
const cartCount = uni.getStorageSync('cartCount') || 0
this.cartCount = cartCount
},
checkLoginStatus() {
// 检查登录状态
const token = uni.getStorageSync('token')
if (!token) {
// 未登录,跳转到登录页
uni.navigateTo({
url: '/pages/login/login'
})
}
},
refreshData() {
// 刷新页面数据
// 注意:不是所有页面都需要在onShow时刷新数据
// 只有那些可能被其他页面影响的数据才需要刷新
}
}
}
3. onReady - 页面初次渲染完成
触发时机:页面初次渲染完成时 执行次数:只执行一次 适合做什么:获取节点信息、设置动画等
javascript
export default {
onReady() {
console.log('页面渲染完成')
// 获取节点信息
this.getElementInfo()
// 设置动画
this.startAnimation()
// 设置页面标题(动态)
uni.setNavigationBarTitle({
title: '动态标题'
})
},
methods: {
getElementInfo() {
// 获取元素信息
const query = uni.createSelectorQuery().in(this)
query.select('.my-element').boundingClientRect(data => {
console.log('元素信息:', data)
}).exec()
},
startAnimation() {
// 开始动画
const animation = uni.createAnimation({
duration: 1000,
timingFunction: 'ease'
})
animation.scale(1.2).rotate(45).step()
this.animationData = animation.export()
}
}
}
4. onHide - 页面隐藏
触发时机:页面隐藏时(跳转到其他页面或应用进入后台) 执行次数:可能多次执行 适合做什么:暂停操作、保存数据等
javascript
export default {
data() {
return {
timer: null,
formData: {}
}
},
onHide() {
console.log('页面隐藏')
// 清除定时器
if (this.timer) {
clearInterval(this.timer)
this.timer = null
}
// 暂停视频播放
this.pauseVideo()
// 保存表单数据
this.saveFormData()
},
methods: {
pauseVideo() {
// 暂停视频播放
const videoContext = uni.createVideoContext('myVideo', this)
videoContext.pause()
},
saveFormData() {
// 保存表单数据到本地
uni.setStorageSync('formData', this.formData)
}
}
}
5. onUnload - 页面卸载
触发时机:页面卸载时(页面被关闭) 执行次数:只执行一次 适合做什么:清理资源、取消网络请求等
javascript
export default {
data() {
return {
requestTask: null,
timer: null
}
},
onUnload() {
console.log('页面卸载')
// 取消网络请求
if (this.requestTask) {
this.requestTask.abort()
}
// 清除定时器
if (this.timer) {
clearInterval(this.timer)
}
// 清理其他资源
this.cleanup()
},
methods: {
cleanup() {
// 清理资源
// 移除事件监听器
// 清理缓存数据等
}
}
}
生命周期执行顺序
让我们通过一个完整的例子来看看生命周期的执行顺序:
javascript
export default {
data() {
return {
message: 'Hello UniApp'
}
},
// 1. 页面加载(只执行一次)
onLoad(options) {
console.log('1. onLoad - 页面加载', options)
},
// 2. 页面显示(可能多次执行)
onShow() {
console.log('2. onShow - 页面显示')
},
// 3. 页面初次渲染完成(只执行一次)
onReady() {
console.log('3. onReady - 页面渲染完成')
},
// 4. 页面隐藏(可能多次执行)
onHide() {
console.log('4. onHide - 页面隐藏')
},
// 5. 页面卸载(只执行一次)
onUnload() {
console.log('5. onUnload - 页面卸载')
}
}
执行顺序:
- 第一次打开页面:
onLoad
→onShow
→onReady
- 跳转到其他页面:
onHide
- 返回这个页面:
onShow
- 关闭这个页面:
onHide
→onUnload
实际应用案例
案例1:商品详情页
javascript
export default {
data() {
return {
productId: '',
productInfo: {},
timer: null,
viewCount: 0
}
},
onLoad(options) {
// 获取商品ID
this.productId = options.id
// 加载商品信息
this.loadProductInfo()
},
onShow() {
// 开始计算浏览时长
this.startViewTimer()
// 刷新库存信息
this.refreshStock()
},
onHide() {
// 停止计算浏览时长
this.stopViewTimer()
// 上报浏览数据
this.reportViewData()
},
onUnload() {
// 清理定时器
this.stopViewTimer()
},
methods: {
async loadProductInfo() {
try {
const res = await uni.request({
url: `/api/product/\${this.productId}`
})
this.productInfo = res.data
} catch (err) {
uni.showToast({
title: '加载失败',
icon: 'error'
})
}
},
startViewTimer() {
this.timer = setInterval(() => {
this.viewCount++
}, 1000)
},
stopViewTimer() {
if (this.timer) {
clearInterval(this.timer)
this.timer = null
}
},
async refreshStock() {
// 刷新库存信息
try {
const res = await uni.request({
url: `/api/product/\${this.productId}/stock`
})
this.productInfo.stock = res.data.stock
} catch (err) {
console.error('刷新库存失败:', err)
}
},
reportViewData() {
// 上报浏览数据
if (this.viewCount > 0) {
uni.request({
url: '/api/report/view',
method: 'POST',
data: {
productId: this.productId,
viewTime: this.viewCount
}
})
}
}
}
}
案例2:聊天页面
javascript
export default {
data() {
return {
messageList: [],
websocket: null,
heartbeatTimer: null
}
},
onLoad(options) {
// 获取聊天室ID
this.roomId = options.roomId
// 加载历史消息
this.loadHistoryMessages()
},
onShow() {
// 连接WebSocket
this.connectWebSocket()
// 标记消息为已读
this.markMessagesAsRead()
},
onHide() {
// 断开WebSocket连接
this.disconnectWebSocket()
},
onUnload() {
// 确保WebSocket已断开
this.disconnectWebSocket()
},
methods: {
connectWebSocket() {
this.websocket = uni.connectSocket({
url: `wss://api.example.com/chat/\${this.roomId}`
})
this.websocket.onMessage((res) => {
const message = JSON.parse(res.data)
this.messageList.push(message)
})
// 开始心跳
this.startHeartbeat()
},
disconnectWebSocket() {
if (this.websocket) {
this.websocket.close()
this.websocket = null
}
// 停止心跳
this.stopHeartbeat()
},
startHeartbeat() {
this.heartbeatTimer = setInterval(() => {
if (this.websocket) {
this.websocket.send({
data: JSON.stringify({ type: 'heartbeat' })
})
}
}, 30000)
},
stopHeartbeat() {
if (this.heartbeatTimer) {
clearInterval(this.heartbeatTimer)
this.heartbeatTimer = null
}
}
}
}
常见问题和最佳实践
1. 不要在onShow中频繁调用API
javascript
// ❌ 错误做法
onShow() {
// 每次显示都调用API,浪费资源
this.loadAllData()
}
// ✅ 正确做法
onShow() {
// 只刷新必要的数据
this.refreshCartCount()
this.checkLoginStatus()
}
2. 及时清理资源
javascript
// ✅ 正确做法
export default {
data() {
return {
timer: null,
requestTask: null
}
},
onHide() {
// 清理定时器
if (this.timer) {
clearInterval(this.timer)
this.timer = null
}
},
onUnload() {
// 取消网络请求
if (this.requestTask) {
this.requestTask.abort()
}
}
}
3. 合理使用生命周期
onLoad
:初始化数据、接收参数onShow
:刷新状态、重新连接onReady
:操作DOM、设置动画onHide
:暂停操作、保存数据onUnload
:清理资源、取消请求
小结
今天我们学习了:
- ✅ 应用生命周期和页面生命周期
- ✅ 每个生命周期的触发时机和适用场景
- ✅ 生命周期的执行顺序
- ✅ 实际应用案例
- ✅ 常见问题和最佳实践
记住这个口诀:
onLoad
:页面出生,初始化onShow
:页面显示,刷新状态onReady
:渲染完成,操作DOMonHide
:页面隐藏,暂停操作onUnload
:页面死亡,清理资源
下一篇预告
下一篇我们将学习《基础组件大全 - 按钮、文本、图片这些怎么用?》,开始学习UniApp的各种组件。
练习作业
- 创建一个页面,在每个生命周期中打印日志
- 实现一个计时器,在页面显示时开始计时,隐藏时停止
- 在页面卸载时弹出确认对话框
生命周期是页面的灵魂,掌握了生命周期,你就能让页面在合适的时机做合适的事情!