uni-app 生命周期
生命周期指一个应用或页面从出生到销毁的过程中,各个节点自动触发的钩子函数。理解生命周期,才知道代码该往哪里放,比如请求数据应该放在哪个钩子里。
uni-app 的生命周期分两层,应用生命周期(整个应用级)和页面生命周期(单个页面级)。
应用生命周期
写在 App.vue 里,整个应用只触发一次:
<script>
export default {
onLaunch() {
console.log('应用初始化完成,全局只触发一次')
// 适合做:检查登录态、读取全局配置、上报启动日志
},
onShow() {
console.log('应用进入前台(从后台切回来也会触发)')
},
onHide() {
console.log('应用进入后台(按 Home 键、切到别的 App)')
}
}
</script>
三个钩子各管什么:
| 钩子 | 触发时机 | 典型用途 |
|---|---|---|
onLaunch |
应用初始化完成,整个生命周期只触发一次 | 检查登录态、初始化全局数据 |
onShow |
应用启动完成、或从后台切回前台 | 刷新全局数据、检查版本 |
onHide |
应用切到后台 | 暂停播放、保存草稿 |
注意:onLaunch 触发时页面还没创建,此时拿不到页面 DOM;想判断首次启动还是从后台回来,可以在 onLaunch 里记一个标志。
页面生命周期
写在每个页面的 <script> 里,页面从打开到关闭完整走一遍:
<script>
export default {
data() {
return { id: '' }
},
onLoad(options) {
console.log('页面加载,可接收跳转参数', options)
},
onShow() {
console.log('页面显示(每次显示都触发)')
},
onReady() {
console.log('页面初次渲染完成,可以操作 DOM 了')
},
onHide() {
console.log('页面被隐藏(跳转到别的页面时)')
},
onUnload() {
console.log('页面卸载(关闭页面时)')
}
}
</script>
触发顺序
| 钩子 | 触发时机 | 触发次数 |
|---|---|---|
onLoad |
页面加载,带跳转参数 | 每次创建触发一次 |
onShow |
页面显示 | 每次显示都触发 |
onReady |
初次渲染完成 | 每次创建触发一次 |
onHide |
页面被覆盖(跳走) | 每次隐藏都触发 |
onUnload |
页面销毁(返回关闭) | 每次关闭触发一次 |
三个钩子的触发次数要记牢,onShow 和 onHide 是每次都会触发,onLoad、onReady、onUnload 只在创建或销毁时触发一次。
生命周期与路由跳转的关系
这是最容易搞混的点。用 navigateTo 跳到新页面时,旧页面不会销毁,只触发 onHide,返回时才触发 onUnload:
打开详情页:
index(onHide) → detail(onLoad → onShow → onReady)
返回首页:
detail(onUnload) → index(onShow)
所以判断「页面是否重新显示」要用 onShow 而不是 onLoad。典型场景,列表页跳详情、详情页改完数据返回,列表页要在 onShow 里重新拉取数据,放 onLoad 里不会生效(页面没销毁,不会再走 onLoad)。
Vue3 的组合式写法
如果你用 <script setup>,生命周期钩子从 @dcloudio/uni-app 里引入,用法是传回调:
<script setup>
import { ref } from 'vue'
import { onLoad, onShow, onUnload } from '@dcloudio/uni-app'
const id = ref('')
onLoad((options) => {
id.value = options.id || ''
})
onShow(() => {
console.log('页面显示')
})
onUnload(() => {
console.log('页面卸载')
})
</script>
高频附加钩子
页面生命周期还有几个高频附加钩子,虽然不在这条主链上,但列表页几乎必用:
<script>
export default {
// 下拉刷新(需在 pages.json 开启 enablePullDownRefresh)
onPullDownRefresh() {
console.log('用户下拉刷新')
// 拉完数据记得关闭动画
uni.stopPullDownRefresh()
},
// 滚动到底部(上拉加载更多)
onReachBottom() {
console.log('滚到底了,加载下一页')
},
// 小程序分享
onShareAppMessage() {
return { title: '分享标题', path: '/pages/index/index' }
}
}
</script>
| 钩子 | 触发时机 | 典型用途 |
|---|---|---|
onPullDownRefresh |
用户下拉刷新 | 重新拉取列表数据 |
onReachBottom |
滚动触底 | 分页加载下一页 |
onShareAppMessage |
用户点分享 | 自定义分享标题和内容 |
onPageScroll |
页面滚动 | 吸顶导航、返回顶部按钮 |
onPullDownRefresh 需要在 pages.json 的页面 style 里开启:
{
"pages": [
{
"path": "pages/index/index",
"style": {
"enablePullDownRefresh": true
}
}
]
}
各平台差异提示
大部分钩子在所有平台行为一致,两个容易踩的差异:
- onShow 在 H5 端,浏览器标签页切换回来时也会触发,如果
onShow里有重逻辑要注意频率。 - onHide 在小程序端,切到后台、跳转到另一个小程序或打开客服等都会触发,写清理逻辑时要考虑这些场景。
综合示例:完整流程演示
首页 → 详情页 → 返回,打印全部钩子看触发顺序:
首页 index.vue:
<script>
export default {
onLoad() {
console.log('index onLoad')
},
onShow() {
console.log('index onShow')
},
onReady() {
console.log('index onReady')
},
onHide() {
console.log('index onHide(被详情页覆盖)')
},
onUnload() {
console.log('index onUnload(页面销毁)')
},
methods: {
goDetail() {
uni.navigateTo({ url: '/pages/detail/detail?id=1' })
}
}
}
</script>
<template>
<button @click="goDetail">打开详情页</button>
</template>
详情页 detail.vue(只打 onLoad 和 onShow):
<script>
export default {
onLoad(options) {
console.log('detail onLoad,参数:', options)
},
onShow() {
console.log('detail onShow')
}
}
</script>
<template>
<view>详情页</view>
</template>
控制台输出顺序:
[页面加载] index onLoad
[页面显示] index onShow
[渲染完成] index onReady
[点击跳转]
[覆盖隐藏] index onHide
[创建页面] detail onLoad,参数:{ id: '1' }
[页面显示] detail onShow
[点击返回]
[销毁页面] detail onUnload
[再次显示] index onShow
对照这张日志就能看出,onShow 每次显示都触发,onLoad 每个页面实例只触发一次,这决定了数据加载放哪。
总结
| 层级 | 钩子 | 常用场景 |
|---|---|---|
| 应用级 | onLaunch / onShow / onHide | 全局初始化、前后台切换 |
| 页面级 | onLoad / onShow / onReady / onHide / onUnload | 参数接收、数据加载 |
| 附加 | onPullDownRefresh / onReachBottom / onShareAppMessage | 刷新、分页、分享 |
一句话总结:页面级生命周期里,onLoad 只走一次用来接参数,onShow 每次显示都走用来刷新数据,onHide 是被盖住,onUnload 是销毁,列表页数据刷新记得放 onShow。