小程序生命周期深度解析
小程序生命周期:应用级(onLaunch/onShow)与页面级(onLoad/onShow/onReady)及页面栈。 讲清各钩子触发时机,掌握后能答出「页面跳转时生命周期如何串联」。
小程序生命周期深度解析
一句话概括
小程序的生命周期有三层嵌套——App → Page → Component,但最容易出 bug 的不是时序复杂,而是开发者搞混了”只执行一次”和”每次显示都执行”这两个类别。
核心知识点
1. 三层生命周期
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// App 生命周期
App({
onLaunch() {}, // 只一次:小程序首次启动
onShow() {}, // 多次:启动 + 从后台切回 + 从其他小程序切回
onHide() {}, // 多次:切后台 / 锁屏 / 来电
});
// Page 生命周期(最重要)
Page({
onLoad(query) {}, // 只一次,带路由参数
onShow() {}, // 多次,每次出现在用户面前
onReady() {}, // 只一次,首次渲染完成
onHide() {}, // 多次,页面被覆盖
onUnload() {}, // 只一次,navigateBack 销毁
});
2. onLoad vs onShow:最易混淆的一对
1
2
3
4
5
6
7
8
9
10
11
// onLoad:只触发一次,带路由参数,适合"初始化"
// onShow:每次可见都触发,不带参数,适合"刷新/恢复"
// 经典 bug 场景:
// 用户在 A 页面填了表单 → 跳到 B 页面选东西 → 返回 A
// 如果表单初始化写在 onLoad 里 → 返回时触发的 onShow 不会重置表单 ✅
// 如果表单数据在 onShow 里重新请求 → 返回时数据被刷新覆盖 ❌
// 正确做法:
onLoad(query) { this.loadData(query.id); } // 核心数据只加载一次
onShow() { this.refreshDynamicData(); } // 只刷新会变化的数据(如库存)
3. 页面栈与生命周期时序
1
2
3
4
5
6
7
// A → navigateTo B
// 时序:B.onLoad → B.onShow → B.onReady → A.onHide
// 关键:B 的三个钩子跑完后,A 才 onHide(不是先隐藏 A 再加载 B)
// B → navigateBack
// 时序:B.onUnload → WebView 销毁 → A.onShow
// 关键:A 的 onLoad 不会再触发!onReady 也不会!
4. Tab 页面的特殊性
1
2
3
4
// Tab 页的 WebView 在启动时就被创建,永远不销毁
// onLoad 只在第一次选中 Tab 时触发
// Tab 切换只触发 onShow/onHide(不会 onUnload)
// 这意味着 Tab 页的 onLoad 可能在 App 启动后很久才触发(用户一直没点那个 Tab)
5. 后台切回:Token 失效的黄金场景
1
2
3
4
5
6
7
8
9
10
11
12
13
14
App({
onShow() {
const lastActive = wx.getStorageSync('_last_active') || 0;
if (Date.now() - lastActive > 30 * 60 * 1000) {
// 超过 30 分钟切回,session 大概率已过期
this.refreshSession();
}
wx.setStorageSync('_last_active', Date.now());
},
onHide() {
// 保存当前状态,防止被杀后台
wx.setStorageSync('_session_snapshot', this.getStateSnapshot());
},
});
其实你每天都在用
- 微信切换账号后的空白:切账号时 App 重新 onLaunch/onShow,之前所有页面的 data 全没了——这就是为什么有些小程序切账号后”数据还在”
- 从公众号消息打开小程序:App.onShow 会被触发——但 App.onLaunch 不会,因为小程序已经启动了
- navigateTo 加 redirectTo:电商支付流程标配——支付成功后 redirectTo 结果页(销毁支付页),防止用户返回到支付页重复支付
- getCurrentPages():这是理解页面栈的万能工具——在 onShow 里调用它,你马上能知道”我从哪个页面过来的”
常见误解(FAQ)
❌ 误区一:”onReady 就是 DOM 准备好了,可以操作节点”
onReady 表示”首次渲染完成”,但不代表所有子组件的 ready 也完成了。如果你需要在 onReady 里通过 selectComponent 获取组件实例并调用其方法,记得那个组件可能还没 ready。安全做法:子组件在 ready 中 triggerEvent(‘componentReady’),父组件收到后再操作。
❌ 误区二:”onHide 里 setData 可以保存用户进度”
onHide 时页面即将被覆盖,setData 的渲染结果用户其实看不到(因为页面已经隐藏了)。保存用户进度应该用 wx.setStorage,而不是依赖 setData。setData 在 onHide 中是浪费渲染资源。
❌ 误区三:”onUnload 里不需要清理,反正页面销毁了”
虽然 WebView 被销毁了,但你在逻辑层的定时器、事件监听、全局存储引用不会自动清理。如果不手动 clearInterval 和 off 事件监听,这些资源会持续占用 JSCore 直到小程序被杀死。onUnload 是清理资源的最后机会。
一句话总结
小程序的 bug 有一半来自生命周期时序——记住”onLoad 只一次,onShow 每次见”这句话,已经能排除 80% 的陷阱。
本文由作者按照 CC BY 4.0 进行授权