UniApp 开发微信小程序,绝大多数内存泄漏、页面逻辑异常、页面塌陷、重复请求、状态错乱,根源都来自生命周期处理不当。
很多开发者只会写onLoad、onShow,对页面什么时候销毁、哪些场景页面不会销毁、资源如何释放理解模糊。 同时在 VibeCoding 模式下,直接让 Claude Code 生成页面,AI 经常忘记销毁定时器、全局事件、订阅、监听,直接埋下内存泄漏隐患。
核心痛点:AI 生成代码只保证功能跑通,不会自动处理生命周期资源释放。我们可以通过标准化提示词 + 项目规则文件,让 Claude Code 主动托管生命周期管理,从源头规避泄漏问题。
本文完整梳理 UniApp 页面 / 组件生命周期、页面销毁判定规则、页面栈机制,同时给出可以直接复制给 Claude Code 的全套提示词,实现 AI 辅助管控生命周期。
一、UniApp 小程序页面生命周期完整流程
注意:UniApp 编译到微信小程序,底层依旧复用微信小程序原生生命周期,uni 做了一层封装,执行顺序和原生小程序保持一致。
页面生命周期完整执行顺序
- onLoad(options) :页面加载,仅执行1 次。页面初始化,获取路由参数;此时 DOM 还未完全渲染。
- onShow() :页面显示,每次页面出现在屏幕都会执行。返回上一页、tab 切换、弹窗关闭露出本页面都会触发。适合刷新页面数据。
- onReady():页面初次渲染完成,DOM 节点就绪,只执行 1 次。可以操作节点、获取元素尺寸。
- onHide():页面隐藏,页面被压入后台,页面没有销毁,只是看不见。跳转新页面、弹出遮罩会触发。
- onUnload() :页面销毁,只执行 1 次。页面实例被释放,所有定时器、事件、订阅必须在这里清除。
关键区分 onHide 和 onUnload(90% 开发者混淆)
onHide:页面只是隐藏,实例还存在内存中,不会销毁,再次回来触发 onShow。onUnload:页面实例彻底销毁,内存回收;销毁之后页面变量全部失效。
致命误区:不要在
onHide清除定时器,onHide 只是隐藏,切回来 onShow 又要重新开启,频繁开关会反复创建定时器。定时器、长连接必须放在onUnload销毁。
二、重点:什么场景页面会销毁?什么场景页面不会销毁
小程序核心:页面栈决定页面是否销毁,页面栈最大限制 10 层。
✅页面会触发 onUnload(页面销毁释放内存)
uni.redirectTo:关闭当前页面,打开新页面 → 当前页面销毁uni.reLaunch:关闭全部页面,打开目标页 → 所有旧页面全部销毁uni.navigateBack()返回上一页 / 多级页面 → 被回退掉的页面执行 onUnload 销毁navigateTo打开新页面,栈满 10 层,最底层页面会被系统销毁
❌页面不会销毁,只触发 onHide(页面驻留内存)
uni.navigateTo跳转新页面:旧页面压入页面栈,不会销毁,仅触发 onHide,返回时 onShow 恢复。
这是最容易内存泄漏的场景!很多人 navigateTo 跳转离开页面,以为页面销毁,实际页面还在内存运行,定时器、监听还在后台跑。
uni.switchTab切换 tab 页面:非 tab 页面会销毁;tabbar 页面常驻内存,永远不会执行 onUnload!
⚠️重中之重:Tabbar 页面,永远不会执行
onUnload! tab 页面生命周期:只会反复执行onShow / onHide,不会销毁。 所以 tab 页面不能依赖 onUnload 去清理定时器、socket、事件监听。tab 页面资源释放要写在 onHide,打开写 onShow。
页面栈行为总结表
表格
| 跳转 API | 原页面生命周期行为 | 页面是否销毁 |
|---|---|---|
| uni.navigateTo | 触发 onHide,不触发 onUnload | ❌驻留内存 |
| uni.redirectTo | 触发 onUnload | ✅销毁 |
| uni.reLaunch | 全部页面 onUnload | ✅全部销毁 |
| uni.navigateBack | 被回退页面执行 onUnload | ✅销毁 |
| uni.switchTab | tab 页面 onShow/onHide;非 tab 页面 onUnload | Tab 页面常驻不销毁 |
三、高频生命周期引发的 BUG 根源
- navigateTo 跳转离开页面,定时器 setInterval 没有销毁 页面隐藏后定时器依旧后台执行,变量已经卸载,引发各种报错,内存持续上涨。
根源:误以为离开页面就会销毁,实际页面还在栈内。
-
Tab 页面把清理逻辑写在 onUnload tab 页面永远不会执行 onUnload,定时器永远不会释放,常驻内存泄漏。
-
onShow 里面重复创建监听,没有销毁 每次 onShow 新增一次事件监听,多次进出页面,监听不断叠加,触发多次回调。
-
onLoad 只执行一次,把接口写在 onLoad,返回页面数据不会刷新 列表页进入详情再返回,页面数据不更新。
-
onUnload 里面操作已经销毁的组件,调用 setData,控制台警告
-
全局事件 \(on / uni.\)on 事件监听,页面卸载没有 $off 取消订阅 页面销毁之后,事件还在监听,再次触发会访问已经销毁页面的 this,产生诡异报错。
四、人工编写生命周期最佳实践(标准规范)
- 定时器、延时器:navigateTo 打开的普通页面:创建保存实例,onUnload 中 clear。
- Tabbar 页面定时器:onShow 开启,onHide 关闭,不要写 onUnload。
- **uni.\(on全局事件**:页面onUnload必须执行uni.\)off 解绑。
- websocket、长轮询:页面销毁必须关闭连接。
- 页面数据请求:一次性初始化放在 onLoad;每次显示需要刷新放在 onShow,注意做防抖避免重复请求。
- 不要在 onUnload 中做复杂网络请求,页面实例销毁,网络回调大概率失效。
错误示范:
onShow(){
setInterval(()=>{ /*业务逻辑*/ },1000)
}
// 没有销毁!每次进入页面新增一个定时器,无限叠加
正确示范
export default {
data(){
return {
timer:null
}
},
onShow(){
this.timer = setInterval(()=>{},1000)
},
onUnload(){
if(this.timer){
clearInterval(this.timer)
this.timer = null
}
uni.$off('customEvent')
}
}
五、Claude Code 托管 UniApp 生命周期:提示词实战
现实痛点:直接让 Claude Code 写 uniapp 页面,AI 经常忘记释放定时器、全局事件,造成隐性内存泄漏。 解决思路:
- 在项目根目录配置
CLAUDE.md写入 UniApp 生命周期强制规范,AI 每次读取项目自动生效。 - 开发页面 / 组件时,带上生命周期管控系统提示词。
- 写完页面,让 AI 做生命周期专项审查。
5.1 项目根目录 CLAUDE.md 片段(直接复制)
bash
# UniApp小程序开发强制规范-生命周期
## 生命周期强制约束
1. 分清各个跳转API的页面栈行为:
- navigateTo跳转,旧页面只onHide,不会onUnload,页面驻留内存;
- tabbar页面**永远不会执行onUnload**;tab页面定时器、监听开启放在onShow,清理释放写在onHide。
2. 所有setInterval、setTimeout,必须保存实例变量,页面对应生命周期清除。
3. 使用uni.$on全局事件,页面销毁必须uni.$off解绑,禁止事件残留。
4. websocket、长轮询,页面退出必须关闭连接。
5. onLoad仅执行一次,只放页面初始化逻辑;每次显示需要刷新的数据,放置onShow。
6. onUnload中禁止发起新网络请求,只做资源清理。
7. 禁止在onHide中清除普通页面定时器,普通页面定时器清理放在onUnload。
8. 生成页面完成之后,输出生命周期自查清单:定时器、延时器、全局事件、长连接是否做释放。
## 页面跳转编码约束
写跳转代码必须选对API:
- 需要保留上一页:navigateTo
- 不需要保留当前页:redirectTo
- 清空全部页面栈:reLaunch
- tab切换:switchTab(不能携带url参数)
5.2 生成新页面时,复制给 Claude Code 完整提示词
bash
你现在来开发UniApp微信小程序页面,严格遵守项目CLAUDE.md中生命周期全部规范。
开发要求:
1. 区分普通页面和tabbar页面,如果是tab页面,定时器、监听开启写onShow,资源释放写onHide,绝对不要依赖onUnload做清理。
2. 所有定时器、延时器必须保存到data变量,在对应生命周期做清除,杜绝内存泄漏。
3. 如果使用uni.$on全局事件监听,必须在页面销毁生命周期执行uni.$off解绑。
4. 路由参数读取放在onLoad;每次页面显示需要刷新业务数据写在onShow,做好防抖,避免重复高频请求。
5. onUnload只做资源清理,不要写业务逻辑、不要发起网络请求。
6. 函数逻辑拆分,不要生命周期内堆砌大量业务代码。
7. 页面代码完成之后,输出一份【生命周期自查报告】,检查点:定时器、延时器、全局事件、websocket、页面跳转API选型是否正确。
输出完整vue页面代码,template、script、style完整。
5.3 对已有页面,让 Claude Code 做生命周期专项审查优化提示词
bash
对当前这个uniapp vue页面做生命周期专项审计,重点排查内存泄漏风险:
1. 找出定时器setInterval / setTimeout,检查是否有对应的销毁逻辑;区分普通页面 / tabbar页面。
2. 查找uni.$on全局事件,确认是否有uni.$off解绑。
3. 检查websocket、长轮询是否关闭。
4. 检查onLoad onShow onHide onUnload逻辑是否放错位置。
5. 检查页面跳转API选型是否合理,navigateTo / redirectTo / reLaunch / switchTab使用是否正确。
输出:问题清单 + 修改之后完整优化代码,每一处改动添加注释说明为什么修改。
六、常见 AI 生成代码坑点(Claude Code 高频踩坑)
- AI 分不清 tabbar 页面生命周期,tab 页面清理逻辑写在 onUnload,资源永远不会释放。
- 生成定时器,不保存实例,直接匿名函数,后续无法 clear。
bash
// AI经常写出这种危险代码,无法销毁
onShow(){
setInterval(()=>{},1000)
}
- 大量使用 uni.\(on,忘记uni.\)off,页面销毁事件还在运行。
- 把刷新接口写在 onLoad,navigateTo 返回页面,数据不会刷新。
- onUnload 里面调用接口,页面销毁,回调报错。
重要提醒:即使写了提示词,AI 依然有概率忽略规则,开发完成,人工快速过一遍 onUnload、onHide 资源释放逻辑,不能完全甩锅 AI。
七、调试手段:验证页面是否真正销毁
- 在 onUnload 添加打印日志,看页面是否执行销毁钩子。
bash
onUnload(){
console.log("页面执行onUnload,页面实例销毁")
}
- 微信开发者工具 → 调试器 → Memory 内存面板,可以观察页面反复进出,内存是否持续上涨,判断是否存在内存泄漏。
- 注意:navigateTo 跳转出去,返回之前页面不会打印 onUnload,属于正常现象。
八、全文总结
navigateTo跳转,旧页面只隐藏,驻留内存,不会销毁;tabbar 页面永远不会触发 onUnload。- 普通页面资源释放写
onUnload;tab 页面资源释放写onHide。 - 定时器、全局事件、长连接,必须配对创建与销毁,是小程序内存泄漏重灾区。
- VibeCoding 场景,Claude Code 不会自动处理生命周期资源释放,需要通过
CLAUDE.md项目规则 + 专项提示词强制约束 AI。 - AI 可以辅助规范化生命周期,但是不能完全替代人工审查,重点检查销毁逻辑。